class BaseFilterView(QLabel): def __init__(self, controller, model): super().__init__() self.controller = controller self.model = model self._init_UI() def _init_UI(self): self.setFixedHeight(120) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.layout = QVBoxLayout() self.layout.setSpacing(2) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setAlignment(Qt.AlignTop) self.setLayout(self.layout) self.title = QLabel() self.title.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.list_view = QListWidget() self.list_view.setFixedHeight(100) self.list_view.setSelectionMode(QAbstractItemView.MultiSelection) self.list_view.itemSelectionChanged.connect(self._on_item_selected) self.layout.addWidget(self.title) self.layout.addWidget(self.list_view) def set_title(self, text: str) -> None: self.title.setText(text) def set_items(self, texts: tuple[str, ...]) -> None: self.list_view.clear() for text in texts: self._add_item(text) def _add_item(self, text: str) -> None: item = QListWidgetItem(text) item.setTextAlignment(Qt.AlignHCenter) self.list_view.addItem(item) def _on_item_selected(self) -> None: selected = [item.text() for item in self.list_view.selectedItems()] self.controller.on_item_selected(selected) def clear_selection(self) -> None: self.list_view.clearSelection()
class BWPassengerWindow(QMdiSubWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setBaseSize(400, 400) self.centralwidget = QWidget(self) self.setWidget(self.centralwidget) layout = QHBoxLayout(self.centralwidget) self.passengerlist = QListWidget(self.centralwidget) layout.addWidget(self.passengerlist) self.setWindowTitle("Passengers") def reset(self): self.passengerlist.clearSelection() self.passengerlist.clear() def add_passenger(self, passenger_name, passenger_id): item = BWEntityEntry(passenger_id, passenger_name) self.passengerlist.addItem(item) def set_title(self, entityname): self.setWindowTitle("Passengers - {0}".format(entityname))
class StatModUseful(QScrollArea): """ This class is not called directly by HABBY, but it is the parent class of EstihabW and FstressW. As fstress and estimhab have a similar graphical user interface, this architecture allows to re-use some functions between the two classes, which saves a bit of coding. """ send_log = pyqtSignal(str, name='send_log') """ PyQtsignal to write the log. """ show_fig = pyqtSignal() """ PyQtsignal to show the figures. """ def __init__(self): super().__init__() self.path_bio = 'biology' self.eq1 = QLineEdit() self.ew1 = QLineEdit() self.eh1 = QLineEdit() self.eq2 = QLineEdit() self.ew2 = QLineEdit() self.eh2 = QLineEdit() self.eqmin = QLineEdit() self.eqmax = QLineEdit() self.target_lineedit_list = [] self.add_qtarget_button = QPushButton() self.add_qtarget_button.setIcon( QIcon(os.path.join(os.getcwd(), "file_dep", "icon", "plus.png"))) self.add_qtarget_button.setStyleSheet( "background-color: rgba(255, 255, 255, 0);") self.add_qtarget_button.setToolTip( self.tr("Click to add one target discharge.")) # self.add_qtarget_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.remove_qtarget_button = QPushButton() self.remove_qtarget_button.setIcon( QIcon(os.path.join(os.getcwd(), "file_dep", "icon", "moins.png"))) self.remove_qtarget_button.setStyleSheet( "background-color: rgba(255, 255, 255, 0);") self.remove_qtarget_button.setToolTip( self.tr("Click to remove one target discharge.")) self.list_f = QListWidget() self.selected_aquatic_animal_qtablewidget = QListWidget() self.msge = QMessageBox() self.fish_selected = [] self.qall = [ ] # q1 q2 qmin qmax q50. Value cannot be added directly because of stathab. def add_fish(self): """ The function is used to select a new fish species (or inverterbrate) """ items = self.list_f.selectedItems() ind = [] if items: for i in range(0, len(items)): # avoid to have the same fish multiple times if items[i].text() in self.fish_selected: pass else: self.fish_selected.append(items[i].text()) # order the list (careful QLIstWidget do not order as sort from list) if self.fish_selected: self.fish_selected.sort() self.selected_aquatic_animal_qtablewidget.clear() self.selected_aquatic_animal_qtablewidget.addItems( self.fish_selected) # bold for selected fish font = QFont() font.setBold(True) for i in range(0, self.list_f.count()): for f in self.fish_selected: if f == self.list_f.item(i).text(): self.list_f.item(i).setFont(font) def remove_fish(self): """ The function is used to remove fish species (or inverterbates species) """ item = self.selected_aquatic_animal_qtablewidget.takeItem( self.selected_aquatic_animal_qtablewidget.currentRow()) try: self.fish_selected.remove(item.text()) except ValueError: pass # bold for selected fish font = QFont() font.setBold(False) for i in range(0, self.list_f.count()): if item.text() == self.list_f.item(i).text(): self.list_f.item(i).setFont(font) item = None def remove_all_fish(self): """ This function removes all fishes from the selected fish """ self.selected_aquatic_animal_qtablewidget.clear() self.list_f.clear() self.fish_selected = [] self.list_f.addItems(self.data_fish[:, 0]) def add_sel_fish(self): """ This function loads the xml file and check if some fish were selected before. If yes, we add them to the list """ # open the xml file filename_path_pro = os.path.join(self.path_prj, self.name_prj + '.habby') if os.path.isfile(filename_path_pro): parser = ET.XMLParser(remove_blank_text=True) doc = ET.parse(filename_path_pro, parser) root = doc.getroot() # get the selected fish child = root.find(".//Habitat/fish_selected") if child is not None: fish_selected_b = child.text if fish_selected_b is not None: if ',' in fish_selected_b: fish_selected_b = fish_selected_b.split(',') # show it for i in range(0, self.list_f.count()): self.list_f.clearSelection() self.list_f.setCurrentRow(i) items = self.list_f.selectedItems() if items: fish_l = items[0].text() if fish_l in fish_selected_b: # do not work with space here self.add_fish() def find_path_im_est(self): """ A function to find the path where to save the figues. Careful there is similar function in sub_and_merge_GUI.py. Do not mix it up :return: path_im a string which indicates the path to the folder where are save the images. """ # to insure the existence of a path path_im = 'no_path' filename_path_pro = os.path.join(self.path_prj, self.name_prj + '.habby') if os.path.isfile(filename_path_pro): parser = ET.XMLParser(remove_blank_text=True) doc = ET.parse(filename_path_pro, parser) root = doc.getroot() child = root.find(".//path_figure") if child is None: path_test = os.path.join(self.path_prj, r'/figures') if os.path.isdir(path_test): path_im = path_test else: path_im = self.path_prj else: path_im = os.path.join(self.path_prj, child.text) else: self.send_log.emit( QCoreApplication.translate("StatModUseful", 'Warning: ') + QCoreApplication.translate( "StatModUseful", "The project is not saved. Save the project in the General tab." )) return path_im def find_path_hdf5_est(self): """ A function to find the path where to save the hdf5 file. Careful a simialar one is in sub_and_merge_GUI.py and in stathab_c. By default, path_hdf5 is in the project folder in the folder 'hdf5'. """ path_hdf5 = 'no_path' filename_path_pro = os.path.join(self.path_prj, self.name_prj + '.habby') if os.path.isfile(filename_path_pro): path_hdf5 = load_project_properties(self.path_prj)["path_hdf5"] # parser = ET.XMLParser(remove_blank_text=True) # doc = ET.parse(filename_path_pro, parser) # root = doc.getroot() # child = root.find(".//path_hdf5") # if child is None: # path_hdf5 = os.path.join(self.path_prj, 'hdf5') # else: # path_hdf5 = os.path.join(self.path_prj, child.text) else: self.send_log.emit( QCoreApplication.translate("StatModUseful", 'Warning: ') + QCoreApplication.translate( "StatModUseful", "The project is not saved. Save the project in the General tab." )) return path_hdf5 def find_path_text_est(self): """ A function to find the path where to save the hdf5 file. Careful a simialar one is in estimhab_GUI.py. By default, path_hdf5 is in the project folder in the folder 'hdf5'. """ # filename_path_pro = os.path.join(self.path_prj, self.name_prj + '.habby') # if os.path.isfile(filename_path_pro): # parser = ET.XMLParser(remove_blank_text=True) # doc = ET.parse(filename_path_pro, parser) # root = doc.getroot() # child = root.find(".//path_text") # if child is None: # path_text = os.path.join(self.path_prj, r'/output/text') # else: # path_text = os.path.join(self.path_prj, child.text) # else: # self.send_log.emit('Warning: ' + QCoreApplication.translate("StatModUseful", "The project is not saved. Save the project in the General tab.")) return os.path.join(self.path_prj, "output", "text") def find_path_output_est(self, att): """ A function to find the path where to save the shapefile, paraview files and other future format. Here, we gave the xml attribute as argument so this functin can be used to find all path needed. However, it is less practical to use as the function above as one should remember the xml tribute to call this function. However, it can be practical to use to add new folder. Careful a similar function is in Hydro_GUI_2.py. :param att: the xml attribute (from the xml project file) linked to the path needed, without the .// """ path_out = 'no_path' filename_path_pro = os.path.join(self.path_prj, self.name_prj + '.habby') if os.path.isfile(filename_path_pro): parser = ET.XMLParser(remove_blank_text=True) doc = ET.parse(filename_path_pro, parser) root = doc.getroot() child = root.find(".//" + att) if child is None: return self.path_prj else: path_out = os.path.join(self.path_prj, child.text) else: self.send_log.emit( QCoreApplication.translate("StatModUseful", 'Warning: ') + QCoreApplication.translate( "StatModUseful", "The project is not saved. Save the project in the General tab." )) return path_out def find_path_input_est(self): """ A function to find the path where to save the input file. Careful a similar one is in sub_and_merge_GUI.py. By default, path_input indicates the folder 'input' in the project folder. """ # path_input = 'no_path' # # filename_path_pro = os.path.join(self.path_prj, self.name_prj + '.habby') # if os.path.isfile(filename_path_pro): # parser = ET.XMLParser(remove_blank_text=True) # doc = ET.parse(filename_path_pro, parser) # root = doc.getroot() # child = root.find(".//path_input") # if child is None: # path_input = os.path.join(self.path_prj, r'/input') # else: # path_input = os.path.join(self.path_prj, child.text) # else: # self.send_log.emit('Warning: ' + QCoreApplication.translate("StatModUseful", "The project is not saved. Save the project in the General tab.")) project_properties = load_project_properties(self.path_prj) path_input = project_properties['path_input'] return path_input def send_err_log(self): """ This function sends the errors and the warnings to the logs. The stdout was redirected to self.mystdout before calling this function. It only sends the hundred first errors to avoid freezing the GUI. A similar function exists in sub_and_merge_GUI.py. Correct both if necessary. """ max_send = 400 if self.mystdout is not None: str_found = self.mystdout.getvalue() else: return str_found = str_found.split('\n') for i in range(0, min(len(str_found), max_send)): if len(str_found[i]) > 1: self.send_log.emit(str_found[i]) if i == max_send - 1: self.send_log.emit( self.fr('Warning: ') + self.tr('Too many information for the GUI.')) def check_all_q(self): """ This function checks the range of the different discharge and send a warning if we are out of the range estimated reasonable (based on the manual from Estimhab and FStress). This is not used by Stathab. It uses the variable self.qall which is a list of float composed of q1, q2, qsim1, qsim2, q50. This function only send warning and it used to check the entry before the calculation. """ if self.qall[0] < self.qall[1]: q1 = self.qall[0] q2 = self.qall[1] else: q2 = self.qall[0] q1 = self.qall[1] if q2 < 2 * q1: self.send_log.emit( QCoreApplication.translate("StatModUseful", 'Warning: ') + QCoreApplication.translate( "StatModUseful", 'Measured discharges are not very different. The results might ' 'not be realistic. \n')) if (self.qall[4] < q1 / 10 or self.qall[4] > 5 * q2 ) and self.qall[4] != -99: # q50 not always necessary self.send_log.emit( QCoreApplication.translate("StatModUseful", 'Warning: ') + QCoreApplication.translate( "StatModUseful", 'Q50 should be between q1/10 and 5*q2 for optimum results.' )) if self.qall[2] < q1 / 10 or self.qall[2] > 5 * q2: self.send_log.emit( QCoreApplication.translate("StatModUseful", 'Warning: ') + QCoreApplication.translate( "StatModUseful", 'Discharge range should be between q1/10 and 5*q2 for optimum results (1).' )) if self.qall[3] < q1 / 10 or self.qall[3] > 5 * q2: self.send_log.emit( QCoreApplication.translate("StatModUseful", 'Warning: ') + QCoreApplication.translate( "StatModUseful", 'Discharge range should be between q1/10 and 5*q2 for optimum results (2).' ))
class ImageDump(QWidget): def __init__(self, parent, title): super().__init__(parent) cwd = os.path.abspath(os.path.dirname(__file__)) self._icons_path = os.path.join(cwd, 'icons') self.initUI(title) def initUI(self, title): ### Add toolbar to widget ### toolLayout = QBoxLayout(QBoxLayout.TopToBottom, self) toolLayout.setContentsMargins(0, 0, 0, 0) toolbar = QToolBar() toolLayout.addWidget(toolbar) ### Add contents ### vbox = QVBoxLayout() self.label = QLabel(self) self.label.setText(title) vbox.addWidget(self.label) self.list = QListWidget(self) self.list.setSelectionMode( self.list.ExtendedSelection) #MultiSelection? self.list.setIconSize(QSize(60, 60)) #self.list.setFlow( QListWidget.LeftToRight ) #self.list.setResizeMode( QListWidget.Adjust ) # TODO: Finish QListWidget settings #self.list.setSpacing( 2 ) #self.list.setViewMode( QListWidget.IconMode ) vbox.addWidget(self.list) ### Put contents in outer toolLayout ### toolLayout.addLayout(vbox) toolLayout.setDirection(QBoxLayout.BottomToTop) ### Tools ### cwd = os.path.abspath(os.path.dirname(__file__)) icons = os.path.join(cwd, 'icons') select_all_act = QtHelper.initAction( self, 'Select All', self.selectAll, status='Select all images in the image set', icon='Select All.png') toolbar.addAction(select_all_act) toolbar.addSeparator() open_act = QtHelper.initAction(self, 'Add Images', self.selectImages, status='Add images to the image set', icon='Images.png') toolbar.addAction(open_act) remove_act = QtHelper.initAction( self, 'Remove Selected Images', self.removeSelected, status='Remove selected images from the image set', icon='Delete.png') toolbar.addAction(remove_act) def addItem(self, text): '''Adds a new QListWidgetItem with label "text".''' if len(self.list.findItems(text, Qt.MatchCaseSensitive)) == 0: item = QListWidgetItem(text) item.setIcon(QIcon(text)) self.list.addItem(item) def removeItem(self, text): '''Remove all QListWidgetItems with label "text" (there should only ever be 1).''' for i in range(self.list.count() - 1, -1, -1): it = self.list.item(i) if it.text() == text: self.list.takeItem(i) def addItems(self, texts): '''Adds a QListWidgetItem labelled by each string in [texts]. Selects the added items.''' self.list.clearSelection() for text in texts: self.addItem(text) self.list.item(self.list.count() - 1).setSelected(True) self.list.sortItems() def removeItems(self, texts): '''Removes all items with labels contained in [texts].''' for i in range(self.list.count() - 1, -1, -1): it = self.list.item(i) if it.text() in texts: self.list.takeItem(i) def getSelected(self): '''Returns a list of text labels corresponding to the currently selected items.''' return [it.text() for it in list(self.list.selectedItems())] def removeSelected(self): '''Removes the items currently selected from the QListWidget.''' selected = self.getSelected() if len(selected) == 0: return msgBox = QMessageBox() msgBox.setText('Are you sure you want to remove the selected images?') msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) pressed = msgBox.exec_() if pressed == QMessageBox.Ok: for text in self.getSelected(): self.removeItem(text) def selectAll(self): '''Selects all items in the QListWidget.''' if len(self.list.selectedItems()) == self.list.count(): self.list.clearSelection() else: self.list.selectAll() def selectImages(self): '''Prompts the user for images and adds them to the current set.''' dialog = QFileDialog(self, caption='Open Images', directory=os.getcwd(), filter='Images (*.png *.jpg)') dialog.setFileMode(QFileDialog.ExistingFiles) if dialog.exec_(): filenames = dialog.selectedFiles() else: filenames = [] self.addItems(filenames)
class MainWindow(QMainWindow): """ 主窗口类 """ def __init__(self): super().__init__() self.item_cnt = 0 self.col = QColor(0, 0, 0) self.w = 1000 self.h = 1000 self.bezier_num = 3 self.bspline_num = 4 # 使用QListWidget来记录已有的图元,并用于选择图元。注:这是图元选择的简单实现方法,更好的实现是在画布中直接用鼠标选择图元 self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(200) # 使用QGraphicsView作为画布 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, self.w, self.h) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(self.w, self.h) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget self.canvas_widget.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) self.canvas_widget.setVerticalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) # 设置菜单栏 self.menubar = self.menuBar() file_menu = self.menubar.addMenu('文件') set_pen_act = file_menu.addAction('设置画笔') reset_canvas_act = file_menu.addAction('重置画布') save_canvas_act = file_menu.addAction('保存画布') exit_act = file_menu.addAction('退出') draw_menu = self.menubar.addMenu('绘制') line_menu = draw_menu.addMenu('线段') line_naive_act = line_menu.addAction('Naive') line_dda_act = line_menu.addAction('DDA') line_bresenham_act = line_menu.addAction('Bresenham') polygon_menu = draw_menu.addMenu('多边形') polygon_dda_act = polygon_menu.addAction('DDA') polygon_bresenham_act = polygon_menu.addAction('Bresenham') ellipse_act = draw_menu.addAction('椭圆') curve_menu = draw_menu.addMenu('曲线') curve_bezier_act = curve_menu.addAction('Bezier') curve_b_spline_act = curve_menu.addAction('B-spline') edit_menu = self.menubar.addMenu('编辑') translate_act = edit_menu.addAction('平移') rotate_act = edit_menu.addAction('旋转') scale_act = edit_menu.addAction('缩放') clip_menu = edit_menu.addMenu('裁剪') clip_cohen_sutherland_act = clip_menu.addAction('Cohen-Sutherland') clip_liang_barsky_act = clip_menu.addAction('Liang-Barsky') extra_menu = self.menubar.addMenu('附加功能') select_item = extra_menu.addAction('选择图元') fill_polygon = extra_menu.addAction('多边形填充') clip_polygon = extra_menu.addAction('多边形裁剪') cancel_item = extra_menu.addAction('撤销') copy_item = extra_menu.addAction('复制') paste_item = extra_menu.addAction('粘贴') # 连接信号和槽函数 exit_act.triggered.connect(qApp.quit) set_pen_act.triggered.connect(self.set_pen_action) reset_canvas_act.triggered.connect(self.reset_canvas_action) save_canvas_act.triggered.connect(self.save_canvas_action) line_naive_act.triggered.connect(self.line_naive_action) line_dda_act.triggered.connect(self.line_dda_action) line_bresenham_act.triggered.connect(self.line_bresenham_action) ellipse_act.triggered.connect(self.ellipse_action) polygon_dda_act.triggered.connect(self.polygon_dda_action) polygon_bresenham_act.triggered.connect(self.polygon_bresenham_action) curve_bezier_act.triggered.connect(self.curve_bezier_action) curve_b_spline_act.triggered.connect(self.curve_b_spline_action) translate_act.triggered.connect(self.translate_action) rotate_act.triggered.connect(self.rotate_action) scale_act.triggered.connect(self.scale_action) clip_cohen_sutherland_act.triggered.connect( self.clip_cohen_sutherland_action) clip_liang_barsky_act.triggered.connect(self.clip_liang_barsky_action) select_item.triggered.connect(self.select_item_action) fill_polygon.triggered.connect(self.polygon_fill_action) clip_polygon.triggered.connect(self.polygon_clip_action) cancel_item.triggered.connect(self.cancel_item_action) copy_item.triggered.connect(self.copy_item_action) paste_item.triggered.connect(self.paste_item_action) self.list_widget.currentTextChanged.connect( self.canvas_widget.selection_changed) # 设置主窗口的布局 self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.statusBar().showMessage('空闲') self.resize(self.w, self.h) self.setWindowTitle('CG Demo') self.setbar = QToolBar() self.addToolBar(Qt.LeftToolBarArea, self.setbar) self.bezier_box = QSpinBox() self.bezier_box.setRange(3, 20) self.bezier_box.setSingleStep(1) self.bezier_box.setValue(self.bezier_num) self.setbar.addWidget(QLabel("Bezier控制点数")) self.setbar.addWidget(self.bezier_box) self.setbar.addSeparator() self.bspline_box = QSpinBox() self.bspline_box.setRange(4, 20) self.bspline_box.setSingleStep(1) self.bspline_box.setValue(self.bspline_num) self.setbar.addWidget(QLabel("B样条阶数")) self.setbar.addWidget(self.bspline_box) self.bezier_box.valueChanged.connect(self.set_bezier_num) self.bspline_box.valueChanged.connect(self.set_bspline_num) self.w = 1000 self.h = 1000 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, self.w, self.h) self.canvas_widget.resize(self.w, self.h) self.canvas_widget.setFixedSize(self.w, self.h) self.statusBar().showMessage('空闲') self.setMaximumHeight(self.h) self.setMaximumWidth(self.w) self.resize(self.w, self.h) def get_id(self): _id = str(self.item_cnt) self.item_cnt += 1 return _id def remove_id(self): if (self.item_cnt > 0): self.item_cnt -= 1 def set_bezier_num(self): self.bezier_num = self.bezier_box.value() self.canvas_widget.set_dot_num('Bezier', self.bezier_num) def set_bspline_num(self): self.bspline_num = self.bspline_box.value() self.canvas_widget.set_dot_num('B-spline', self.bspline_num) def set_pen_action(self): self.statusBar().showMessage('设置画笔') col = QColorDialog.getColor() if col.isValid(): self.col = col self.list_widget.clearSelection() self.canvas_widget.clear_selection() def reset_canvas_action(self): self.item_cnt = 0 self.statusBar().showMessage('重置画布') dialog = QDialog() dialog.setWindowTitle('重置画布') formlayout = QFormLayout(dialog) box1 = QSpinBox(dialog) box1.setRange(100, 1000) box1.setSingleStep(1) box1.setValue(self.w) slider1 = QSlider(Qt.Horizontal) slider1.setRange(100, 1000) slider1.setSingleStep(1) slider1.setValue(self.w) slider1.setTickPosition(QSlider.TicksBelow) slider1.setTickInterval(100) box2 = QSpinBox(dialog) box2.setRange(100, 1000) box2.setSingleStep(1) box2.setValue(self.h) slider2 = QSlider(Qt.Horizontal) slider2.setRange(100, 1000) slider2.setSingleStep(1) slider2.setValue(self.h) slider2.setTickPosition(QSlider.TicksBelow) slider2.setTickInterval(100) slider1.valueChanged.connect(box1.setValue) box1.valueChanged.connect(slider1.setValue) slider2.valueChanged.connect(box2.setValue) box2.valueChanged.connect(slider2.setValue) formlayout.addRow('Width:', box1) formlayout.addRow(slider1) formlayout.addRow('Height:', box2) formlayout.addRow(slider2) box3 = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) box3.accepted.connect(dialog.accept) box3.rejected.connect(dialog.reject) formlayout.addRow(box3) if dialog.exec(): if (box1.value() < 100 or box1.value() > 1000 or box2.value() < 100 or box2.value() > 1000): QMessageBox.about(self, "提示", "修改失败,请保证输入的数字大于等于100小于等于1000") else: self.w = box1.value() self.h = box2.value() self.canvas_widget.clear_canvas() self.list_widget.clearSelection() self.canvas_widget.clear_selection() self.list_widget.clear() self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, self.w, self.h) self.canvas_widget.resize(self.w, self.h) self.canvas_widget.setFixedSize(self.w, self.h) self.statusBar().showMessage('空闲') self.setMaximumHeight(self.h) self.setMaximumWidth(self.w) self.resize(self.w, self.h) self.canvas_widget.undo_num = 0 self.canvas_widget.undo_save = [] def save_canvas_action(self): self.list_widget.clearSelection() self.canvas_widget.clear_selection() self.statusBar().showMessage('保存画布') dialog = QFileDialog() filename = dialog.getSaveFileName( filter="Image Files(*.jpg *.png *.bmp)") if filename[0]: self.canvas_widget.save_canvas(filename[0], self.w, self.h) #res=self.canvas_widget.grab(self.canvas_widget.sceneRect().toRect()) #res.save(filename[0]) def line_naive_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_line('Naive', self.get_id()) self.statusBar().showMessage('Naive算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_dda_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_line('DDA', self.get_id()) self.statusBar().showMessage('DDA算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_bresenham_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_line('Bresenham', self.get_id()) self.statusBar().showMessage('Bresenham算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def ellipse_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_ellipse(self.get_id()) self.statusBar().showMessage('中点圆生成算法绘制椭圆') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_dda_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_polygon('DDA', self.get_id()) self.statusBar().showMessage('DDA算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_bresenham_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_polygon('Bresenham', self.get_id()) self.statusBar().showMessage('Bresenham算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_bezier_action(self): '''text, ok = QInputDialog.getText(self, 'Bezier算法绘制曲线', '请输入控制点数目:') if ok: n=int(str(text)) else: return if(n<=1): QMessageBox.about(self, "提示", "请保证输入的数字大于1") return''' if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_curve('Bezier', self.get_id(), self.bezier_num) self.statusBar().showMessage('Bezier算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_b_spline_action(self): '''text, ok = QInputDialog.getText(self, 'B-spline算法绘制曲线', '请输入控制点数目:') if ok: n=int(str(text)) else: return if(n<=3): QMessageBox.about(self, "提示", "请保证输入的数字大于3") return''' if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_curve('B-spline', self.get_id(), self.bspline_num) self.statusBar().showMessage('B-spline算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def translate_action(self): self.canvas_widget.start_translate() self.statusBar().showMessage('平移') def rotate_action(self): self.canvas_widget.start_rotate() self.statusBar().showMessage('旋转') def scale_action(self): self.canvas_widget.start_scale() self.statusBar().showMessage('缩放') def clip_cohen_sutherland_action(self): self.canvas_widget.start_clip('Cohen-Sutherland') self.statusBar().showMessage('裁剪') def clip_liang_barsky_action(self): self.canvas_widget.start_clip('Liang-Barsky') self.statusBar().showMessage('裁剪') def select_item_action(self): self.canvas_widget.start_select() self.statusBar().showMessage('选择图元') def copy_item_action(self): self.canvas_widget.start_copy() self.statusBar().showMessage('复制') def paste_item_action(self): self.canvas_widget.start_paste() self.statusBar().showMessage('粘贴') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def cancel_item_action(self): self.statusBar().showMessage('撤销') self.list_widget.clearSelection() self.canvas_widget.clear_selection() #self.canvas_widget.start_cancel(self.item_cnt) self.canvas_widget.start_undo() def polygon_clip_action(self): self.canvas_widget.start_clip_polygon() self.statusBar().showMessage('多边形裁剪') def polygon_fill_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_fill_polygon(self.get_id()) self.statusBar().showMessage('多边形填充') self.list_widget.clearSelection() self.canvas_widget.clear_selection()
class WallpaperWidget(QWizardPage): def __init__(self, parent=None): super().__init__(parent) self.setSubTitle(self.tr("<h2>Choose Wallpaper</h2>")) vlayout = QVBoxLayout(self) labelLayout = QHBoxLayout() labelImage = QLabel() labelImage.setMaximumSize(64, 64) labelImage.setPixmap( QIcon.fromTheme("preferences-desktop-wallpaper").pixmap(64, 64)) labelLayout.addWidget(labelImage) label = QLabel(self) label.setText( self. tr("<p>Choose your favorite wallpaper for Pisi Linux. Don't forget to check out \ <strong>Desktop Settings</strong> for downloading new and cool wallpapers.</p>" )) label.setWordWrap(True) labelLayout.addWidget(label) vlayout.addLayout(labelLayout) vlayout.addItem( QSpacerItem(20, 40, QSizePolicy.Preferred, QSizePolicy.Preferred)) groupBox = QGroupBox(self) groupBox.setTitle(self.tr("Wallpapers")) groupBox.setMinimumHeight(350) grLayout = QVBoxLayout(groupBox) self.listWidget = QListWidget() self.listWidget.setViewMode(QListView.IconMode) self.listWidget.setIconSize(QSize(250, 150)) grLayout.addWidget(self.listWidget) vlayout.addWidget(groupBox) vlayout.addItem( QSpacerItem(20, 40, QSizePolicy.Preferred, QSizePolicy.Preferred)) hlayout = QHBoxLayout() self.button = QPushButton() self.button.setText(self.tr("Choose wallpaper from file")) hlayout.addWidget(self.button) hlayout.addItem( QSpacerItem(400, 20, QSizePolicy.Preferred, QSizePolicy.Preferred)) self.checkbox = QCheckBox() self.checkbox.setText(self.tr("Don't change wallpaper")) hlayout.addWidget(self.checkbox) vlayout.addLayout(hlayout) self.checkbox.clicked.connect(self.wallpaperChecked) self.button.clicked.connect(self.wallpaperSelectDialog) self.listWidget.itemClicked.connect(self.wallpaperSelect) self.selectWallpaper = None self.wallpapersParser() def wallpapersParser(self): wallpaperPath = "/usr/share/wallpapers" for folder in os.listdir(wallpaperPath): path = join(wallpaperPath, folder, "contents") thumbFolder = os.listdir(path) for thumb in thumbFolder: if thumb.startswith("scre"): item = QListWidgetItem(self.listWidget) pix = QPixmap(join(path, thumb)) pix = pix.scaled(QSize(240, 140), Qt.IgnoreAspectRatio, Qt.FastTransformation) item.setIcon(QIcon(pix)) item.setSizeHint(QSize(250, 150)) item.screenshotPath = join(path, thumb) def wallpaperSelect(self, item): if hasattr(item, "userSelect"): self.selectWallpaper = item.screenshotPath else: path = join(dirname(abspath(item.screenshotPath)), "images") list = os.listdir(path) list.sort() self.selectWallpaper = join(path, list[-1]) def wallpaperChecked(self): if self.checkbox.isChecked(): self.selectWallpaper = None self.listWidget.setDisabled(True) self.button.setDisabled(True) else: self.listWidget.clearSelection() self.listWidget.setEnabled(True) self.button.setEnabled(True) def wallpaperSelectDialog(self): file_url, file_type = QFileDialog.getOpenFileName( self, self.tr("Choose wallpaper"), QDir.homePath(), "Image (*.png *.jpg)") print(file_url) if not "" == file_url: self.selectWallpaper = file_url item = QListWidgetItem(self.listWidget) item.setIcon(QIcon(file_url)) item.screenshotPath = file_url item.userSelect = True self.listWidget.setCurrentItem(item) def execute(self): configFilePath = join(QDir.homePath(), ".config", "plasma-org.kde.plasma.desktop-appletsrc") parser = Parser(configFilePath) getWallpaper = parser.getWallpaper() if self.selectWallpaper: if "file://" + self.selectWallpaper != getWallpaper[2]: parser.setWallpaper("file://" + self.selectWallpaper) wp_isin = False appletsrc = open(configFilePath).readlines() for lines in appletsrc: if "Wallpaper" in lines: wp_isin = True wp = "\n[Containments][52][Wallpaper][org.kde.image][General]\nImage=file://{!s}\n".format( self.selectWallpaper) if wp_isin: if self.selectWallpaper: if "file://" + self.selectWallpaper != getWallpaper[1]: parser.setWallpaper("file://" + self.selectWallpaper) else: if self.selectWallpaper: with open(configFilePath, "a") as rcfile: rcfile.write(wp)
class SpellCheckDialog(QDialog): """Dialog to perform and control the spell check operation. """ misspellFound = pyqtSignal() changeRequest = pyqtSignal(str) def __init__(self, spellCheckInterface, parent=None): """Create the dialog. Arguments: spellCheckInterface -- a reference to the spell engine interface parent -- the parent dialog """ super().__init__(parent) self.setWindowFlags(Qt.Dialog | Qt.WindowTitleHint | Qt.WindowCloseButtonHint) self.setWindowTitle(_('Spell Check')) self.spellCheckInterface = spellCheckInterface self.textLineIter = None self.textLine = '' self.replaceAllDict = {} self.tmpIgnoreWords = set() self.word = '' self.postion = 0 topLayout = QHBoxLayout(self) leftLayout = QVBoxLayout() topLayout.addLayout(leftLayout) wordBox = QGroupBox(_('Not in Dictionary')) leftLayout.addWidget(wordBox) wordLayout = QVBoxLayout(wordBox) label = QLabel(_('Word:')) wordLayout.addWidget(label) self.wordEdit = QLineEdit() wordLayout.addWidget(self.wordEdit) self.wordEdit.textChanged.connect(self.updateFromWord) wordLayout.addSpacing(5) label = QLabel(_('Context:')) wordLayout.addWidget(label) self.contextEdit = SpellContextEdit() wordLayout.addWidget(self.contextEdit) self.contextEdit.textChanged.connect(self.updateFromContext) suggestBox = QGroupBox(_('Suggestions')) leftLayout.addWidget(suggestBox) suggestLayout = QVBoxLayout(suggestBox) self.suggestList = QListWidget() suggestLayout.addWidget(self.suggestList) self.suggestList.itemDoubleClicked.connect(self.replace) rightLayout = QVBoxLayout() topLayout.addLayout(rightLayout) ignoreButton = QPushButton(_('Ignor&e')) rightLayout.addWidget(ignoreButton) ignoreButton.clicked.connect(self.ignore) ignoreAllButton = QPushButton(_('&Ignore All')) rightLayout.addWidget(ignoreAllButton) ignoreAllButton.clicked.connect(self.ignoreAll) rightLayout.addStretch() addButton = QPushButton(_('&Add')) rightLayout.addWidget(addButton) addButton.clicked.connect(self.add) addLowerButton = QPushButton(_('Add &Lowercase')) rightLayout.addWidget(addLowerButton) addLowerButton.clicked.connect(self.addLower) rightLayout.addStretch() replaceButton = QPushButton(_('&Replace')) rightLayout.addWidget(replaceButton) replaceButton.clicked.connect(self.replace) self.replaceAllButton = QPushButton(_('Re&place All')) rightLayout.addWidget(self.replaceAllButton) self.replaceAllButton.clicked.connect(self.replaceAll) rightLayout.addStretch() cancelButton = QPushButton(_('&Cancel')) rightLayout.addWidget(cancelButton) cancelButton.clicked.connect(self.reject) self.widgetDisableList = [ ignoreButton, ignoreAllButton, addButton, addLowerButton, self.suggestList ] self.fullDisableList = (self.widgetDisableList + [self.replaceAllButton, self.wordEdit]) def startSpellCheck(self, textLineIter): """Spell check text lines given in the iterator. Block execution except for the dialog if mispellings are found. Return True if spell check completes, False if cancelled. Arguments: textLineIter -- an iterator of text lines to check """ self.textLineIter = textLineIter try: self.textLine = next(self.textLineIter) except StopIteration: return True if self.spellCheck(): if self.exec_() == QDialog.Rejected: return False return True def continueSpellCheck(self): """Check lines, starting with current line. Exit the dialog if there are no more lines to check. """ if not self.spellCheck(): self.accept() def spellCheck(self): """Step through the iterator and spell check the lines. If results found, update the dialog with the results and return True. Return false if the end of the iterator is reached. """ while True: results = self.spellCheckInterface.checkLine( self.textLine, self.tmpIgnoreWords) if results: self.word, self.position, suggestions = results[0] newWord = self.replaceAllDict.get(self.word, '') if newWord: self.textLine = self.replaceWord(newWord) self.changeRequest.emit(self.textLine) else: self.misspellFound.emit() self.setWord(suggestions) return True try: self.textLine = next(self.textLineIter) self.tmpIgnoreWords.clear() except StopIteration: return False def setWord(self, suggestions): """Set dialog contents from the checked line and spell check results. Arguments: suggestions -- a list of suggested replacement words """ self.wordEdit.blockSignals(True) self.wordEdit.setText(self.word) self.wordEdit.blockSignals(False) self.contextEdit.blockSignals(True) self.contextEdit.setPlainText(self.textLine) self.contextEdit.setSelection(self.position, self.position + len(self.word)) self.contextEdit.blockSignals(False) self.suggestList.clear() self.suggestList.addItems(suggestions) self.suggestList.setCurrentItem(self.suggestList.item(0)) for widget in self.fullDisableList: widget.setEnabled(True) def replaceWord(self, newWord): """Return textLine with word replaced with newWord. Arguments: newWord -- the replacement word """ return (self.textLine[:self.position] + newWord + self.textLine[self.position + len(self.word):]) def ignore(self): """Set word to ignored (this check only) and continue spell check. """ self.tmpIgnoreWords.add(self.word) self.continueSpellCheck() def ignoreAll(self): """Add to dictionary's ignore list and continue spell check. """ self.spellCheckInterface.acceptWord(self.word) self.continueSpellCheck() def add(self): """Add misspelling to dictionary and continue spell check""" self.spellCheckInterface.addToDict(self.word, False) self.continueSpellCheck() def addLower(self): """Add misspelling to dictionary as lowercase and continue spell check. """ self.spellCheckInterface.addToDict(self.word, True) self.continueSpellCheck() def replace(self): """Replace misspelled word with suggestion or context edit box Then continue spell check. """ if self.suggestList.isEnabled(): newWord = self.suggestList.currentItem().text() self.textLine = self.replaceWord(newWord) else: self.textLine = self.contextEdit.toPlainText() self.changeRequest.emit(self.textLine) self.continueSpellCheck() def replaceAll(self): """Replace misspelled word with suggestion or word edit (in future too). Stores changed word in replaceAllDict and continues spell check. """ if self.suggestList.isEnabled(): newWord = self.suggestList.currentItem().text() else: newWord = self.wordEdit.text() self.textLine = self.replaceWord(newWord) self.replaceAllDict[self.word] = newWord self.changeRequest.emit(self.textLine) self.continueSpellCheck() def updateFromWord(self): """Update dialog after word line editor change. Disables suggests and ignore/add controls. Updates the context editor. """ for widget in self.widgetDisableList: widget.setEnabled(False) newWord = self.wordEdit.text() self.suggestList.clearSelection() self.contextEdit.blockSignals(True) self.contextEdit.setPlainText(self.replaceWord(newWord)) self.contextEdit.setSelection(self.position, self.position + len(newWord)) self.contextEdit.blockSignals(False) def updateFromContext(self): """Update dialog after context editor change. Disables controls except for replace. """ for widget in self.fullDisableList: widget.setEnabled(False) self.suggestList.clearSelection()
class MainWindow(QMainWindow): """ 主窗口类 """ def __init__(self): super().__init__() # 使用QListWidget来记录已有的图元,并用于选择图元。 #注:这是图元选择的简单实现方法,更好的实现是在画布中直接用鼠标选择图元 self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(200) # 使用QGraphicsView作为画布 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, 600, 600) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(600, 600) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget self.canvas_widget.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) self.canvas_widget.setVerticalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) # 设置菜单栏 menubar = self.menuBar() file_menu = menubar.addMenu('文件') set_pen_act = file_menu.addAction('设置画笔') reset_canvas_act = file_menu.addAction('重置画布') save_canvas_act = file_menu.addAction('保存画布') exit_act = file_menu.addAction('退出') draw_menu = menubar.addMenu('绘制') line_menu = draw_menu.addMenu('线段') line_naive_act = line_menu.addAction('Naive') line_dda_act = line_menu.addAction('DDA') line_bresenham_act = line_menu.addAction('Bresenham') polygon_menu = draw_menu.addMenu('多边形') polygon_dda_act = polygon_menu.addAction('DDA') polygon_bresenham_act = polygon_menu.addAction('Bresenham') ellipse_act = draw_menu.addAction('椭圆') curve_menu = draw_menu.addMenu('曲线') curve_bezier_act = curve_menu.addAction('Bezier') curve_b_spline_act = curve_menu.addAction('B-spline') edit_menu = menubar.addMenu('编辑') translate_act = edit_menu.addAction('平移') rotate_act = edit_menu.addAction('旋转') scale_act = edit_menu.addAction('缩放') clip_menu = edit_menu.addMenu('裁剪') clip_cohen_sutherland_act = clip_menu.addAction('Cohen-Sutherland') clip_liang_barsky_act = clip_menu.addAction('Liang-Barsky') # 连接信号和槽函数 set_pen_act.triggered.connect(self.set_pen_action) reset_canvas_act.triggered.connect(self.reset_canvas_action) save_canvas_act.triggered.connect(self.save_canvas_action) exit_act.triggered.connect(self.exit_action) line_naive_act.triggered.connect(self.line_naive_action) line_dda_act.triggered.connect(self.line_dda_action) line_bresenham_act.triggered.connect(self.line_bresenham_action) polygon_dda_act.triggered.connect(self.polygon_dda_action) polygon_bresenham_act.triggered.connect(self.polygon_bresenham_action) ellipse_act.triggered.connect(self.ellipse_action) curve_bezier_act.triggered.connect(self.curve_bezier_action) curve_b_spline_act.triggered.connect(self.curve_b_spline_action) translate_act.triggered.connect(self.translate_action) rotate_act.triggered.connect(self.rotate_action) scale_act.triggered.connect(self.scale_action) clip_cohen_sutherland_act.triggered.connect( self.clip_cohen_sutherland_action) clip_liang_barsky_act.triggered.connect(self.clip_liang_barsky_action) self.list_widget.currentTextChanged.connect( self.canvas_widget.selection_changed) # 设置主窗口的布局 self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.statusBar().showMessage('空闲') self.resize(600, 600) self.setWindowTitle('CG Demo') def set_pen_action(self): self.statusBar().showMessage('设置画笔') color = QColorDialog.getColor() self.canvas_widget.set_pen_color(color) self.list_widget.clearSelection() self.canvas_widget.clear_selection() def reset_canvas_action(self): self.statusBar().showMessage('重置画布') self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(200) self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, 600, 600) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(600, 600) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget self.canvas_widget.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) self.canvas_widget.setVerticalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) self.list_widget.currentTextChanged.connect( self.canvas_widget.selection_changed) self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.resize(600, 600) self.setWindowTitle('CG Demo') def save_canvas_action(self): self.statusBar().showMessage('保存画布') self.list_widget.clearSelection() self.canvas_widget.clear_selection() file_dialog = QFileDialog() filename = file_dialog.getSaveFileName( filter="Image Files(*.jpg *.png *.bmp)") if filename[0]: ret = self.canvas_widget.grab( self.canvas_widget.sceneRect().toRect()) ret.save(filename[0]) def exit_action(self): self.statusBar().showMessage('退出') reply = QMessageBox.question(self, "CG Demo", "是否保存画布", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.save_canvas_action() qApp.quit() def line_naive_action(self): self.statusBar().showMessage('Naive算法绘制线段') self.canvas_widget.start_draw('line', 'Naive') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_dda_action(self): self.statusBar().showMessage('DDA算法绘制线段') self.canvas_widget.start_draw('line', 'DDA') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_bresenham_action(self): self.statusBar().showMessage('Bresenham算法绘制线段') self.canvas_widget.start_draw('line', 'Bresenham') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def ellipse_action(self): self.statusBar().showMessage('中点圆算法绘制椭圆') self.canvas_widget.start_draw('ellipse') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_dda_action(self): self.statusBar().showMessage('DDA算法绘制多边形') self.canvas_widget.start_draw('polygon', 'DDA') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_bresenham_action(self): self.statusBar().showMessage('Bresenham算法绘制多边形') self.canvas_widget.start_draw('polygon', 'Bresenham') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_bezier_action(self): self.statusBar().showMessage('Bezier算法绘制曲线') self.canvas_widget.start_draw('curve', 'Bezier') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_b_spline_action(self): self.statusBar().showMessage('B-spline算法绘制曲线') self.canvas_widget.start_draw('curve', 'B-spline') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def translate_action(self): self.statusBar().showMessage('平移') self.canvas_widget.start_modify('translate') def rotate_action(self): self.statusBar().showMessage('旋转') self.canvas_widget.start_modify('rotate') def scale_action(self): self.statusBar().showMessage('缩放') self.canvas_widget.start_modify('scale') def clip_cohen_sutherland_action(self): self.statusBar().showMessage('Cohen_Sutherland算法裁剪') self.canvas_widget.start_modify('clip', 'Cohen-Sutherland') def clip_liang_barsky_action(self): self.statusBar().showMessage('Liang_Barsky算法裁剪') self.canvas_widget.start_modify('clip', 'Liang-Barsky')
class MainWindow(QMainWindow): """ 主窗口类 """ def __init__(self): super().__init__() self.item_cnt = 0 # TODO: 区分长宽 # self.size = 900 self.length = 800 self.width = 800 self.is_modified = False self.opened_filename = '' # 使用QListWidget来记录已有的图元,并用于选择图元。注:这是图元选择的简单实现方法,更好的实现是在画布中直接用鼠标选择图元 self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(150) # self.list_widget.setMaximumWidth(150) # self.list_widget.setFixedWidth(150) # 使用QGraphicsView作为画布 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, self.length, self.width) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(self.length, self.width) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget # 设置菜单栏 menubar = self.menuBar() file_menu = menubar.addMenu('文件') set_pen_act = file_menu.addAction('设置画笔') open_canvas_act = file_menu.addAction('打开画布') open_canvas_act.setShortcut('Ctrl+O') reset_canvas_act = file_menu.addAction('重置画布') reset_canvas_act.setShortcut('Ctrl+R') save_canvas_act = file_menu.addAction('保存画布') save_canvas_act.setShortcut('Ctrl+S') export_canvas_act = file_menu.addAction('导出画布') export_canvas_act.setShortcut('Ctrl+E') exit_act = file_menu.addAction('退出') draw_menu = menubar.addMenu('绘制') line_menu = draw_menu.addMenu('线段') line_naive_act = line_menu.addAction('Naive') line_dda_act = line_menu.addAction('DDA') line_bresenham_act = line_menu.addAction('Bresenham') polygon_menu = draw_menu.addMenu('多边形') polygon_dda_act = polygon_menu.addAction('DDA') polygon_bresenham_act = polygon_menu.addAction('Bresenham') ellipse_act = draw_menu.addAction('椭圆') curve_menu = draw_menu.addMenu('曲线') curve_bezier_act = curve_menu.addAction('Bezier') curve_b_spline_act = curve_menu.addAction('B-spline') freedom_act = draw_menu.addAction('自由绘图') edit_menu = menubar.addMenu('编辑') translate_act = edit_menu.addAction('平移') rotate_act = edit_menu.addAction('旋转') scale_act = edit_menu.addAction('缩放') clip_menu = edit_menu.addMenu('裁剪') delete_act = edit_menu.addAction('删除') delete_act.setShortcut('Delete') clip_cohen_sutherland_act = clip_menu.addAction('Cohen-Sutherland') clip_liang_barsky_act = clip_menu.addAction('Liang-Barsky') # 连接信号和槽函数 exit_act.triggered.connect(self.my_quit) # exit_act.triggered.connect(self.close) set_pen_act.triggered.connect(self.set_pen_action) open_canvas_act.triggered.connect(self.open_canvas_action) reset_canvas_act.triggered.connect(lambda: self.reset_canvas_action()) save_canvas_act.triggered.connect(self.save_canvas_action) export_canvas_act.triggered.connect(self.export_canvas_action) line_naive_act.triggered.connect(self.line_naive_action) line_dda_act.triggered.connect(self.line_dda_action) line_bresenham_act.triggered.connect(self.line_bresenham_action) polygon_dda_act.triggered.connect(self.polygon_dda_action) polygon_bresenham_act.triggered.connect(self.polygon_bresenham_action) ellipse_act.triggered.connect(self.ellipse_action) curve_bezier_act.triggered.connect(self.curve_bezier_action) curve_b_spline_act.triggered.connect(self.curve_b_spline_action) translate_act.triggered.connect(self.translate_action) rotate_act.triggered.connect(self.rotate_action) scale_act.triggered.connect(self.scale_action) clip_cohen_sutherland_act.triggered.connect( self.clip_cohen_sutherland_action) clip_liang_barsky_act.triggered.connect(self.clip_liang_barsky_action) delete_act.triggered.connect(self.delete_action) freedom_act.triggered.connect(self.freedom_action) self.list_widget.currentTextChanged.connect( self.canvas_widget.selection_changed) # 设置主窗口的布局 self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.statusBar().showMessage('空闲') # self.resize(self.size, self.size) self.resize(self.length, self.width) self.setWindowTitle('CG Demo') def get_id(self, add): # _id = str(self.item_cnt) if add: self.item_cnt += 1 _id = str(self.item_cnt) return _id def set_pen_action(self): temp_color = QColorDialog.getColor() if temp_color.isValid(): self.canvas_widget.temp_color = temp_color def open_canvas_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.statusBar().showMessage('打开画布') if len(self.canvas_widget.item_dict) > 0 or self.is_modified: reply = QMessageBox.question( self, '是否保存', '是否保存当前草稿?', QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Yes) # print(reply) if reply == QMessageBox.Yes: self.save_canvas_action() elif reply == QMessageBox.Cancel: return self.reset_canvas_action(False) self.statusBar().showMessage('打开画布') path = QFileDialog.getOpenFileName(caption='打开画布', filter='画布文件 (*.canvas)') # print(path[0]) self.opened_filename = path[0] if path[0] != '': fr = open(path[0], 'rb') open_list = pickle.load(fr) for item in open_list: color = QColor(item[4][0], item[4][1], item[4][2]) temp_item = MyItem(item[0], item[1], item[2], item[3], color) self.canvas_widget.scene().addItem(temp_item) self.list_widget.addItem(item[0]) self.canvas_widget.item_dict[item[0]] = temp_item fr.close() self.item_cnt = len(open_list) name = self.opened_filename.split('/')[-1].split('.')[0] self.setWindowTitle('CG Demo - ' + name) def reset_canvas_action(self, resize=True): # print(resize) if resize: self.length = QInputDialog.getInt(self, '请输入', '长度', 800, 200, 1500)[0] self.width = QInputDialog.getInt(self, '请输入', '宽度', 800, 200, 900)[0] self.list_widget.clearSelection() self.list_widget.clear() self.canvas_widget.clear_selection() self.canvas_widget.item_dict.clear() self.canvas_widget.scene().clear() self.item_cnt = 0 self.canvas_widget.status = '' self.opened_filename = '' self.is_modified = False # TODO: 设置长宽 self.scene.setSceneRect(0, 0, self.length, self.width) self.canvas_widget.setFixedSize(self.length, self.width) self.setWindowTitle('CG Demo') # self.resize(self.length, self.width) # self.canvas_widget.updateScene([self.canvas_widget.sceneRect()]) def save_canvas_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.statusBar().showMessage('保存画布') save_list = [] if self.opened_filename == '': path = QFileDialog.getSaveFileName(caption='保存画布', filter='画布文件 (*.canvas)') # print(path[0]) if path[0] != '': for item in self.canvas_widget.item_dict.values(): save_list.append([ item.id, item.item_type, item.p_list, item.algorithm, item.color.getRgb() ]) fw = open(path[0], 'wb') pickle.dump(save_list, fw) fw.close() self.opened_filename = path[0] self.is_modified = False name = self.opened_filename.split('/')[-1].split('.')[0] self.setWindowTitle('CG Demo - ' + name) else: for item in self.canvas_widget.item_dict.values(): save_list.append([ item.id, item.item_type, item.p_list, item.algorithm, item.color.getRgb() ]) fw = open(self.opened_filename, 'wb') pickle.dump(save_list, fw) fw.close() self.is_modified = False def export_canvas_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.statusBar().showMessage('导出画布') # TODO: 区分长宽 self.start_export(self.length, self.width) def my_quit(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() if self.is_modified: reply = QMessageBox.question( self, '退出 - 是否保存', '是否保存当前草稿?', QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Yes) if reply == QMessageBox.Yes: self.save_canvas_action() qApp.quit() elif reply == QMessageBox.No: qApp.quit() else: qApp.quit() def line_naive_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_draw_line('Naive', self.get_id(False)) self.statusBar().showMessage('Naive算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_dda_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_draw_line('DDA', self.get_id(False)) self.statusBar().showMessage('DDA算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_bresenham_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_draw_line('Bresenham', self.get_id(False)) self.statusBar().showMessage('Bresenham算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_dda_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_draw_polygon('DDA', self.get_id(False)) self.statusBar().showMessage('DDA算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_bresenham_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_draw_polygon('Bresenham', self.get_id(False)) self.statusBar().showMessage('Bresenham算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def ellipse_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_draw_ellipse('Midpoint-circle', self.get_id(False)) self.statusBar().showMessage('中点生成算法绘制椭圆') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_bezier_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_draw_curve('Bezier', self.get_id(False)) self.statusBar().showMessage('Bezier曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_b_spline_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_draw_curve('B-spline', self.get_id(False)) self.statusBar().showMessage('B-spline曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def translate_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_translate() self.statusBar().showMessage('平移') def rotate_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_rotate() self.statusBar().showMessage('旋转') def scale_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_scale() self.statusBar().showMessage('缩放') def clip_cohen_sutherland_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_clip('Cohen-Sutherland') self.statusBar().showMessage('Cohen-Sutherland裁剪') def clip_liang_barsky_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_clip('Liang-Barsky') self.statusBar().showMessage('Liang-Barsky裁剪') def delete_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_delete() self.statusBar().showMessage('删除') def freedom_action(self): if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() self.canvas_widget.start_freedom(self.get_id(False)) self.statusBar().showMessage('自由绘图') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def start_export(self, length, width): # TODO: 区分长宽 path = QFileDialog.getSaveFileName( caption='导出画布', filter='BMP图像 (*.bmp);;PNG图像 (*.png);;JPEG图像 (*.jpg)') # print(path) if path[0] != '': canvas = np.zeros([width, length, 3], np.uint8) canvas.fill(255) for item in self.canvas_widget.item_dict.values(): if item.item_type == 'line': pixels = alg.draw_line(item.p_list, item.algorithm) temp_color = item.color.getRgb() pen_color = np.zeros(3, np.uint8) pen_color[0], pen_color[1], pen_color[2] = temp_color[ 0], temp_color[1], temp_color[2] for x, y in pixels: if 0 <= x < length and 0 <= y < width: canvas[y, x] = np.array(pen_color) elif item.item_type == 'polygon': pixels = alg.draw_polygon(item.p_list, item.algorithm) temp_color = item.color.getRgb() pen_color = np.zeros(3, np.uint8) pen_color[0], pen_color[1], pen_color[2] = temp_color[ 0], temp_color[1], temp_color[2] for x, y in pixels: if 0 <= x < length and 0 <= y < width: canvas[y, x] = np.array(pen_color) elif item.item_type == 'ellipse': pixels = alg.draw_ellipse(item.p_list) temp_color = item.color.getRgb() pen_color = np.zeros(3, np.uint8) pen_color[0], pen_color[1], pen_color[2] = temp_color[ 0], temp_color[1], temp_color[2] for x, y in pixels: if 0 <= x < length and 0 <= y < width: canvas[y, x] = np.array(pen_color) elif item.item_type == 'curve': pixels = alg.draw_curve(item.p_list, item.algorithm) temp_color = item.color.getRgb() pen_color = np.zeros(3, np.uint8) pen_color[0], pen_color[1], pen_color[2] = temp_color[ 0], temp_color[1], temp_color[2] for x, y in pixels: if 0 <= x < length and 0 <= y < width: canvas[y, x] = np.array(pen_color) elif item.item_type == 'freedom': pixels = [] for i in range(len(item.p_list) - 1): pixels.extend( alg.draw_line([item.p_list[i], item.p_list[i + 1]], 'Bresenham')) temp_color = item.color.getRgb() pen_color = np.zeros(3, np.uint8) pen_color[0], pen_color[1], pen_color[2] = temp_color[ 0], temp_color[1], temp_color[2] for x, y in pixels: if 0 <= x < length and 0 <= y < width: canvas[y, x] = np.array(pen_color) if path[1] == 'BMP图像 (*.bmp)': Image.fromarray(canvas).save(path[0], 'bmp') elif path[1] == 'PNG图像 (*.png)': Image.fromarray(canvas).save(path[0], 'png') else: Image.fromarray(canvas).save(path[0], 'jpeg', quality=95) def closeEvent(self, a0: QCloseEvent) -> None: if self.canvas_widget.status == 'polygon' or self.canvas_widget.status == 'curve': self.canvas_widget.finish_draw() if self.is_modified: reply = QMessageBox.question( self, '退出 - 是否保存', '是否保存当前草稿?', QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Yes) if reply == QMessageBox.Yes: self.save_canvas_action() a0.accept() elif reply == QMessageBox.Cancel: a0.ignore()
class WallpaperWidget(QWizardPage): def __init__(self, parent=None): super().__init__(parent) self.setSubTitle(self.tr("<h2>Choose Wallpaper</h2>")) vlayout = QVBoxLayout(self) labelLayout = QHBoxLayout() labelImage = QLabel() labelImage.setMaximumSize(64,64) labelImage.setPixmap(QIcon.fromTheme("preferences-desktop-wallpaper").pixmap(64, 64)) labelLayout.addWidget(labelImage) label = QLabel(self) label.setText(self.tr("<p>Choose your favorite wallpaper for KaOS. Don't forget to check out \ <strong>Desktop Settings</strong> for downloading new and cool wallpapers.</p>")) label.setWordWrap(True) labelLayout.addWidget(label) vlayout.addLayout(labelLayout) vlayout.addItem(QSpacerItem(20, 40, QSizePolicy.Preferred, QSizePolicy.Preferred)) groupBox = QGroupBox(self) groupBox.setTitle(self.tr("Wallpapers")) groupBox.setMinimumHeight(350) grLayout = QVBoxLayout(groupBox) self.listWidget = QListWidget() self.listWidget.setViewMode(QListView.IconMode) self.listWidget.setIconSize(QSize(250, 150)) grLayout.addWidget(self.listWidget) vlayout.addWidget(groupBox) vlayout.addItem(QSpacerItem(20, 40, QSizePolicy.Preferred, QSizePolicy.Preferred)) hlayout = QHBoxLayout() self.button = QPushButton() self.button.setText(self.tr("Choose wallpaper from file")) hlayout.addWidget(self.button) hlayout.addItem(QSpacerItem(400, 20, QSizePolicy.Preferred, QSizePolicy.Preferred)) self.checkbox = QCheckBox() self.checkbox.setText(self.tr("Don't change wallpaper")) hlayout.addWidget(self.checkbox) vlayout.addLayout(hlayout) self.checkbox.clicked.connect(self.wallpaperChecked) self.button.clicked.connect(self.wallpaperSelectDialog) self.listWidget.itemClicked.connect(self.wallpaperSelect) self.selectWallpaper = None self.wallpapersParser() def wallpapersParser(self): wallpaperPath = "/usr/share/wallpapers" for folder in os.listdir(wallpaperPath): path = join(wallpaperPath,folder, "contents") thumbFolder = os.listdir(path) for thumb in thumbFolder: if thumb.startswith("scre"): item = QListWidgetItem(self.listWidget) pix = QPixmap(join(path, thumb)) pix = pix.scaled(QSize(240, 140), Qt.IgnoreAspectRatio, Qt.FastTransformation) item.setIcon(QIcon(pix)) item.setSizeHint(QSize(250, 150)) item.screenshotPath = join(path, thumb) def wallpaperSelect(self, item): if hasattr(item, "userSelect"): self.selectWallpaper = item.screenshotPath else: path = join(dirname(abspath(item.screenshotPath)), "images") list = os.listdir(path) list.sort() self.selectWallpaper = join(path, list[-1]) def wallpaperChecked(self): if self.checkbox.isChecked(): self.selectWallpaper = None self.listWidget.setDisabled(True) self.button.setDisabled(True) else: self.listWidget.clearSelection() self.listWidget.setEnabled(True) self.button.setEnabled(True) def wallpaperSelectDialog(self): file_url, file_type = QFileDialog.getOpenFileName(self, self.tr("Choose wallpaper"), QDir.homePath(), "Image (*.png *.jpg)") print(file_url) if not "" == file_url: self.selectWallpaper = file_url item = QListWidgetItem(self.listWidget) item.setIcon(QIcon(file_url)) item.screenshotPath = file_url item.userSelect = True self.listWidget.setCurrentItem(item) def execute(self): configFilePath = join(QDir.homePath(), ".config", "plasma-org.kde.plasma.desktop-appletsrc") parser = Parser(configFilePath) getWallpaper = parser.getWallpaper() if self.selectWallpaper: if "file://"+self.selectWallpaper != getWallpaper[1]: parser.setWallpaper("file://"+self.selectWallpaper)
class MainWindow(QMainWindow): """ 主窗口类 """ def __init__(self): super().__init__() self.item_cnt = 0 # 使用QListWidget来记录已有的图元,并用于选择图元。注:这是图元选择的简单实现方法,更好的实现是在画布中直接用鼠标选择图元 self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(200) # 使用QGraphicsView作为画布 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, 600, 600) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(600, 600) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget # 设置菜单栏 menubar = self.menuBar() file_menu = menubar.addMenu('文件') set_pen_act = file_menu.addAction('设置画笔') reset_canvas_act = file_menu.addAction('重置画布') save_canvas_act = file_menu.addAction('保存画布') exit_act = file_menu.addAction('退出') draw_menu = menubar.addMenu('绘制') line_menu = draw_menu.addMenu('线段') line_dda_act = line_menu.addAction('DDA') line_bresenham_act = line_menu.addAction('Bresenham') polygon_menu = draw_menu.addMenu('多边形') polygon_dda_act = polygon_menu.addAction('DDA') polygon_bresenham_act = polygon_menu.addAction('Bresenham') ellipse_act = draw_menu.addAction('椭圆') curve_menu = draw_menu.addMenu('曲线') curve_bezier_act = curve_menu.addAction('Bezier') curve_b_spline_act = curve_menu.addAction('B-spline') edit_menu = menubar.addMenu('编辑') translate_act = edit_menu.addAction('平移') rotate_act = edit_menu.addAction('旋转') scale_act = edit_menu.addAction('缩放') clip_menu = edit_menu.addMenu('裁剪') clip_cohen_sutherland_act = clip_menu.addAction('Cohen-Sutherland') clip_liang_barsky_act = clip_menu.addAction('Liang-Barsky') # 连接信号和槽函数 exit_act.triggered.connect(qApp.quit) set_pen_act.triggered.connect(self.set_pen_action) reset_canvas_act.triggered.connect(self.reset_canvas_action) save_canvas_act.triggered.connect(self.save_canvas_action) line_dda_act.triggered.connect(self.line_dda_action) line_bresenham_act.triggered.connect(self.line_bresenham_action) polygon_dda_act.triggered.connect(self.polygon_dda_action) polygon_bresenham_act.triggered.connect(self.polygon_bresenham_action) ellipse_act.triggered.connect(self.ellipse_action) curve_bezier_act.triggered.connect(self.curve_bezier_action) curve_b_spline_act.triggered.connect(self.curve_b_spline_action) translate_act.triggered.connect(self.translate_action) rotate_act.triggered.connect(self.rotate_action) scale_act.triggered.connect(self.scale_action) self.list_widget.currentTextChanged.connect( self.canvas_widget.selection_changed) # 设置主窗口的布局 self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.statusBar().showMessage('空闲') self.resize(600, 600) self.setWindowTitle('CG Demo') def get_id(self): _id = str(self.item_cnt) self.item_cnt += 1 return _id def set_pen_action(self): color = QColorDialog.getColor(self.canvas_widget.get_pen()) self.canvas_widget.set_pen(color) def reset_canvas_action(self): self.item_cnt = 0 self.canvas_widget.clear_canvas() self.statusBar().showMessage('重置画布') self.list_widget.clearSelection() self.canvas_widget.clear_selection() self.list_widget.clear() def save_canvas_action(self): result = QFileDialog.getSaveFileName( filter='Images (*.png *.jpg *.bmp)') self.canvas_widget.save_canvas(result[0]) self.statusBar().showMessage('保存画布') def line_dda_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_line('DDA', self.get_id()) self.statusBar().showMessage('DDA算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_bresenham_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_line('Bresenham', self.get_id()) self.statusBar().showMessage('Bresenham算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_dda_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_polygon('DDA', self.get_id()) self.statusBar().showMessage('DDA算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_bresenham_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_polygon('Bresenham', self.get_id()) self.statusBar().showMessage('Bresenham算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def ellipse_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_ellipse(self.get_id()) self.statusBar().showMessage('中点圆生成算法绘制椭圆') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_bezier_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_curve('Bezier', self.get_id()) self.statusBar().showMessage('Bezier算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_b_spline_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_curve('B-spline', self.get_id()) self.statusBar().showMessage('B-spline算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def translate_action(self): self.canvas_widget.start_translate() pass def rotate_action(self): self.canvas_widget.start_rotate() pass def scale_action(self): self.canvas_widget.start_scale() pass
class Example(QWidget): def __init__(self): super().__init__() self.filenames = json_files() if type(self.filenames) is list: self.curr_file = self.filenames[0] else: self.curr_file = self.filenames self.initUI() def initUI(self): self.num = -1 # index for search bar query self.show_save = False # bool for showing unsaved changes dialog self.temp_file = ".temp.csv" self.refresh_file = False # failsafe 1 for itemChanged trigger self.list_1 = QListWidget() lister(file=self.curr_file, target=self.list_1, index=0, mode=0) self.list_1.clicked.connect(self.clear_selection) self.list_1.installEventFilter(self) self.list_items = self.list_1.count( ) # failsafe 2 for itemChanged trigger self.list_1.itemChanged.connect(self.edit_next_item) self.list_1.verticalScrollBar().valueChanged.connect(self.sync_scroll) self.list_2 = QListWidget() lister(file=self.curr_file, target=self.list_2, index=1, mode=0) self.list_2.clicked.connect(self.clear_selection) self.list_3 = QListWidget() lister(file=self.curr_file, target=self.list_3, index=2, mode=0) self.list_3.clicked.connect(self.clear_selection) self.all_lists = [self.list_1, self.list_2, self.list_3] self.menubar = QMenuBar() self.menubar.setNativeMenuBar(False) exit_event = QAction('Exit', self) exit_event.setShortcut('Ctrl+W') exit_event.triggered.connect(app.quit) showAct = QAction('Show extras', self, checkable=True) showAct.setChecked(False) showAct.setShortcut('Ctrl+E') showAct.triggered.connect(self.hide_notes) addAct = QAction('Fields', self) addAct.setShortcut('Ctrl+N') addAct.triggered.connect(self.add_item) fileOpen = QAction('Open file', self) fileOpen.triggered.connect(self.fileDialog) fileOpen.setShortcut('Ctrl+O') fileSave = QAction('Save file', self) fileSave.triggered.connect(self.save) fileSave.triggered.connect(self.refresh_recents) fileSave.setShortcut('Ctrl+S') self.fileRecents = QMenu('Recent file', self) self.refresh_recents() self.toggle_theme = QAction('Toggle theme', self, checkable=True) self.toggle_theme.setChecked(json_theme()) self.toggle_theme.triggered.connect(self.theme) self.toggle_theme.setShortcut('Ctrl+T') self.col_sort_index = QMenu('Sorting column index', self) self.col_sort_index.addAction(QAction(str(0), self)) self.col_sort_index.addAction(QAction(str(1), self)) self.col_sort_index.addAction(QAction(str(2), self)) self.col_sort_index.triggered.connect(self.sort_col_choice) self.col_search_index = QMenu('Searching column index', self) self.col_search_index.addAction(QAction(str(0), self)) self.col_search_index.addAction(QAction(str(1), self)) self.col_search_index.addAction(QAction(str(2), self)) self.col_search_index.triggered.connect(self.search_col_choice) self.sort = QAction('Sort entries', self, checkable=True) self.curr_col = 0 self.search_col = 0 self.sort.triggered.connect(self.refresh_list) self.sort.setShortcut('Ctrl+R') self.addFields = self.menubar.addMenu('Add') self.addFields.addAction(addAct) self.optionMenu = self.menubar.addMenu('Options') self.optionMenu.addAction(exit_event) self.optionMenu.addAction(showAct) self.optionMenu.addAction(self.toggle_theme) self.optionMenu.addMenu(self.col_sort_index) self.optionMenu.addMenu(self.col_search_index) self.optionMenu.addAction(self.sort) self.fileMenu = self.menubar.addMenu('File') self.fileMenu.addAction(fileOpen) self.fileMenu.addAction(fileSave) self.fileMenu.addMenu(self.fileRecents) self.search_bar = QLineEdit() self.search_bar.setPlaceholderText('Search vocab') self.search_bar.setClearButtonEnabled(True) self.search_bar.setMaxLength(10) self.search_bar.returnPressed.connect(self.search_item) self.status_bar = QStatusBar() status(self.status_bar, self.list_1) grid = QGridLayout() grid.setSpacing(10) grid.addWidget(self.menubar, 0, 0) grid.addWidget(self.list_1, 1, 0) grid.addWidget(self.list_2, 1, 1) grid.addWidget(self.list_3, 1, 2) grid.addWidget(self.search_bar, 0, 1) grid.addWidget(self.status_bar) self.theme() self.setLayout(grid) self.setGeometry(*json_window_size()) self.setWindowTitle(f'{split_name(self.curr_file)}') self.show() self.list_1.scrollToBottom() self.list_2.verticalScrollBar().setHidden(True) self.list_3.verticalScrollBar().setHidden(True) self.list_3.setHidden(True) def sync_scroll(self): scroll_location = self.list_1.verticalScrollBar().value() self.list_2.verticalScrollBar().setValue(scroll_location) self.list_3.verticalScrollBar().setValue(scroll_location) def edit_next_item(self, event): """When an item is added and edited on the first col, starts editing its counterpart on the next col""" if self.list_items == self.list_1.count( ) - 2 or self.list_items != self.list_1.count( ) and self.refresh_file == False: item = self.list_2.item(self.list_2.count() - 1) self.list_2.editItem(item) self.list_items = self.list_1.count() def closeEvent(self, event): """Triggered upon program exit, shows a dialog for unsaved changes using a bool""" if self.show_save == True: reply = QMessageBox.question( self, 'Message', "You may have unsaved changes, are you sure you want to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: try: remove(self.temp_file) except: pass event.accept() else: event.ignore() else: pass def sort_col_choice(self, action): self.curr_col = int(action.text()) def search_col_choice(self, action): self.search_col = int(action.text()) def refresh_list(self): """Refreshes the contents of the lists, when sorting is used""" self.save( mode=1 ) # saves a temp copy, with changes, but irreversable sorting introduced clear_lists(self.all_lists) if self.sort.isChecked() == True: mode = 2 else: mode = 0 try: lister(file=self.temp_file, target=self.list_1, index=0, mode=mode, column=self.curr_col) lister(file=self.temp_file, target=self.list_2, index=1, mode=mode, column=self.curr_col) lister(file=self.temp_file, target=self.list_3, index=2, mode=mode, column=self.curr_col) except: lister(file=self.curr_file, target=self.list_1, index=0, mode=mode, column=self.curr_col) lister(file=self.curr_file, target=self.list_2, index=1, mode=mode, column=self.curr_col) lister(file=self.curr_file, target=self.list_3, index=2, mode=mode, column=self.curr_col) def refresh_recents(self): try: file_1 = QAction(self.curr_file, self) self.fileRecents.addAction(file_1) file_1.triggered.connect(self.clickedFileAct) if type(self.filenames) is list: if self.filenames[1] != None: file_2 = QAction(self.filenames[1], self) self.fileRecents.addAction(file_2) file_2.triggered.connect(self.clickedFileAct) if self.filenames[2] != None: file_3 = QAction(self.filenames[2], self) self.fileRecents.addAction(file_3) file_3.triggered.connect(self.clickedFileAct) except: pass def clickedFileAct(self): self.refresh_file = True file = self.sender().text() self.curr_file = file self.setWindowTitle(f'{split_name(self.curr_file)}') clear_lists(self.all_lists) lister(file=self.curr_file, target=self.list_1, index=0) lister(file=self.curr_file, target=self.list_2, index=1) lister(file=self.curr_file, target=self.list_3, index=2) status(self.status_bar, self.list_1) self.theme() self.list_1.scrollToBottom() self.list_3.setHidden(True) self.refresh_file = False def eventFilter(self, source, event): """Item (row) deletion""" if (event.type() == QEvent.ContextMenu and source is self.list_1): menu = QMenu() menu.addAction("Delete row") if menu.exec_(event.globalPos()): item = source.itemAt(event.pos()) try: model = self.list_1.indexFromItem(item) row = model.row() self.show_save = True self.list_1.takeItem(row) self.list_2.takeItem(row) self.list_3.takeItem(row) status(self.status_bar, self.list_1, f'Deleted row number: {row+1}.') self.clearSelection() except: pass return True return super(Example, self).eventFilter(source, event) def hide_notes(self): """Toggles showing the note column and stretches the window for clearer reading of it""" self.list_3.setHidden(not self.list_3.isHidden()) def theme(self): """Sets the theme for the window and its widgets""" palette = QPalette() # dark theme if self.toggle_theme.isChecked() == True: palette.setColor(QPalette.Window, QColor(0, 0, 0)) dark = "background-color: rgb(0, 0, 0); color: rgb(255, 255, 255);" self.menubar.setStyleSheet(dark) self.addFields.setStyleSheet(dark) self.optionMenu.setStyleSheet(dark) self.fileMenu.setStyleSheet(dark) self.search_bar.setStyleSheet( "background-color: rgb(0, 0, 0); color: rgb(255, 255, 255)" ) # border: 0px; for transparency self.status_bar.setStyleSheet(dark) style_items(self.all_lists, dark_theme=True) # light theme elif self.toggle_theme.isChecked() == False: palette.setColor(QPalette.Window, QColor(255, 255, 255)) light = "background-color: rgb(255, 255, 255); color: rgb(0, 0, 0)" self.menubar.setStyleSheet(light) self.addFields.setStyleSheet(light) self.optionMenu.setStyleSheet(light) self.fileMenu.setStyleSheet(light) self.search_bar.setStyleSheet(light) self.status_bar.setStyleSheet(light) style_items(self.all_lists, dark_theme=False) self.setPalette(palette) self.theme_bool = self.toggle_theme.isChecked( ) # used in the save func def search_item(self): """Takes input from the search bar and matches with an item, gets index and scrolls to it, more reusults being qued with the num class var """ query = self.search_bar.text() search = self.all_lists[self.search_col].findItems( query, Qt.MatchContains) status(self.status_bar, self.list_1, f'Found {len(search)} results.') self.clear_selection() # testing search in all column # search_list =[] # for x in range(3): # search_list.append(self.all_lists[x].findItems(query, Qt.MatchContains)) # parent_list = [] # for x in range(3): # for y in range(len(search_list[x])): # parent_list.append(self.all_lists[x]) # replace with x # import itertools # merged = list(itertools.chain.from_iterable(search_list)) # search_dict = dict(zip(parent_list, merged)) # print(search_dict) # print() # print(len(merged)) # print(len(parent_list)) self.num += 1 for i in search: try: model_index = self.all_lists[self.search_col].indexFromItem( search[self.num]) except: self.num = 0 model_index = self.all_lists[self.search_col].indexFromItem( search[self.num]) item_index = model_index.row() self.all_lists[self.search_col].item(item_index).setSelected(True) self.list_1.scrollToItem(self.list_1.item(item_index), QAbstractItemView.PositionAtCenter) def add_item(self): self.show_save = True for x in range(3): if x == 0: lister(file=self.curr_file, target=self.list_1, index=x, mode=1) elif x == 1: lister(file=self.curr_file, target=self.list_2, index=x, mode=1) elif x == 2: lister(file=self.curr_file, target=self.list_3, index=x, mode=1) item = self.list_1.item(self.list_1.count() - 1) self.list_1.editItem(item) status(self.status_bar, self.list_1) self.list_1.scrollToBottom() self.list_2.scrollToBottom() self.list_3.scrollToBottom() def clear_selection(self): """Clears all item slections for aesthetical purposes, but only single clicks""" self.list_1.clearSelection() self.list_2.clearSelection() self.list_3.clearSelection() def fileDialog(self): fname = QFileDialog() path = fname.getOpenFileName(self, 'Open file', getcwd(), filter='csv (*.csv);;') if path[0] == '': # failsafe for canceling the dialog return self.curr_file self.curr_file = path[0] self.setWindowTitle(f'{split_name(self.curr_file)}') clear_lists(self.all_lists) lister(file=self.curr_file, target=self.list_1, index=0) lister(file=self.curr_file, target=self.list_2, index=1) lister(file=self.curr_file, target=self.list_3, index=2) status(self.status_bar, self.list_1) self.theme() def save(self, mode=0): self.show_save = False list1_items = items_text(self.list_1) list2_items = items_text(self.list_2) list3_items = items_text(self.list_3) total_dicts = [] for (a, b, c) in zip(list1_items, list2_items, list3_items): # each letter is a column dictionary = {'word_1': a, 'word_2': b, 'notes': c} total_dicts.append(dictionary) if mode == 0: writer(file=self.curr_file, data=total_dicts) status(self.status_bar, self.list_1, ('Saved current changes.')) try: json_template(theme=self.theme_bool, files=[self.curr_file, None, None], window_size=self.geometry().getRect() ) # current size values of the window except: json_template( ) # bug cannot be avoided, even though used setChecked at the beggining elif mode == 1: self.show_save = True writer(file=self.temp_file, data=total_dicts) # avoids stacking and refreshes recent file actions actions = self.fileRecents.actions() for action in actions: self.fileRecents.removeAction(action)
class ListUI(QWidget): def __init__(self, socman): super(ListUI, self).__init__() # create objects; self.__socman = socman self.__ob_list_main = QListWidget() self.__ob_line_search = QLineEdit() self.__ob_vlay_main = QVBoxLayout() self.__ob_hlay_main = QHBoxLayout() self.__ob_button_back = Button("Назад") self.__ob_button_delete = Button("Удалить", 1) self.buttonClicked = 0 self.doubleClicked = 0 # config; self.setLayout(self.__ob_vlay_main) self.setFixedSize(700, 500) self.__ob_vlay_main.setSpacing(0) self.__ob_vlay_main.addWidget(self.__ob_line_search) self.__ob_vlay_main.addWidget(self.__ob_list_main) self.__ob_vlay_main.addLayout(self.__ob_hlay_main) self.__ob_hlay_main.addWidget(self.__ob_button_back, 4) self.__ob_hlay_main.addWidget(self.__ob_button_delete, 1) self.__ob_line_search.setStyleSheet("background: rgb(170, 170, 170); border: none;") self.__ob_line_search.setFixedHeight(50) self.__ob_line_search.textChanged.connect(self.__onLineEdit) self.__ob_list_main.setStyleSheet("background: rgb(200, 200, 200); border: none;") self.__ob_list_main.setMinimumWidth(self.__ob_list_main.sizeHintForColumn(0)) self.__ob_list_main.itemDoubleClicked.connect(self.__onDoubleClicked) self.__ob_list_main.itemClicked.connect(self.__onItemClicked) self.__ob_list_main.setSelectionMode(QListWidget.MultiSelection) self.__ob_button_back.clicked.connect(self.__onButtonClicked) self.__ob_button_back.setFixedHeight(50) self.__ob_button_delete.hide() self.__ob_button_delete.clicked.connect(self.__onButtonDeleteClicked) def showData(self): self.loadList() self.show() def __onButtonClicked(self): self.buttonClicked() def __onLineEdit(self): self.__ob_button_delete.hide() if len(self.__ob_line_search.text()): self.__ob_list_main.clear() for i in self.__socman.getDump(): if self.__ob_line_search.text().upper() in i.getTitle().upper(): item = QListWidgetItem() item.setText(i.getTitle()) item.setSizeHint(QSize(10, 30)) self.__ob_list_main.addItem(item) else: self.loadList() def __onDoubleClicked(self, item): self.__ob_list_main.clearSelection() for i in self.__socman.getDump(): if item.text() == i.getTitle(): self.doubleClicked(i) break def __onItemClicked(self): if len(self.__ob_list_main.selectedItems()): self.__ob_button_delete.show() else: self.__ob_button_delete.hide() def __onButtonDeleteClicked(self): for i in self.__ob_list_main.selectedItems(): for item in self.__socman.getDump(): if i.text() == item.getTitle(): self.__socman.delete(item) break self.__ob_button_delete.hide() self.loadList() def loadList(self): self.__ob_list_main.clear() self.__ob_line_search.clear() for i in self.__socman.getDump(): item = QListWidgetItem() item.setText(i.getTitle()) item.setSizeHint(QSize(10, 30)) self.__ob_list_main.addItem(item)
class _HistoryDialog: record_label = "Save..." execute_label = "Execute" def __init__(self, controller, typed_only): # make dialog hidden initially self.controller = controller self.typed_only = typed_only self.window = controller.tool_window.create_child_window( "Command History", close_destroys=False) self.window.fill_context_menu = self.fill_context_menu parent = self.window.ui_area from PyQt5.QtWidgets import QListWidget, QVBoxLayout, QFrame, QHBoxLayout, QPushButton, QLabel self.listbox = QListWidget(parent) self.listbox.setSelectionMode(QListWidget.ExtendedSelection) self.listbox.itemSelectionChanged.connect(self.select) main_layout = QVBoxLayout(parent) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(self.listbox) num_cmd_frame = QFrame(parent) main_layout.addWidget(num_cmd_frame) num_cmd_layout = QHBoxLayout(num_cmd_frame) num_cmd_layout.setContentsMargins(0, 0, 0, 0) remem_label = QLabel("Remember") from PyQt5.QtCore import Qt remem_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) num_cmd_layout.addWidget(remem_label, 1) from PyQt5.QtWidgets import QSpinBox, QSizePolicy class ShorterQSpinBox(QSpinBox): max_val = 1000000 def textFromValue(self, val): # kludge to make the damn entry field shorter if val == self.max_val: return "1 mil" return str(val) spin_box = ShorterQSpinBox() spin_box.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) spin_box.setRange(100, spin_box.max_val) spin_box.setSingleStep(100) spin_box.setValue(controller.settings.num_remembered) spin_box.valueChanged.connect(self._num_remembered_changed) num_cmd_layout.addWidget(spin_box, 0) num_cmd_layout.addWidget(QLabel("commands"), 1) num_cmd_frame.setLayout(num_cmd_layout) button_frame = QFrame(parent) main_layout.addWidget(button_frame) button_layout = QHBoxLayout(button_frame) button_layout.setContentsMargins(0, 0, 0, 0) for but_name in [ self.record_label, self.execute_label, "Delete", "Copy", "Help" ]: but = QPushButton(but_name, button_frame) but.setAutoDefault(False) but.clicked.connect( lambda arg, txt=but_name: self.button_clicked(txt)) button_layout.addWidget(but) button_frame.setLayout(button_layout) self.window.manage(placement=None, initially_hidden=True) from chimerax.core.history import FIFOHistory self._history = FIFOHistory(controller.settings.num_remembered, controller.session, "commands") self._record_dialog = None self._search_cache = (False, None) def add(self, item, *, typed=False): if len(self._history) >= self.controller.settings.num_remembered: if not self.typed_only or self._history[0][1]: self.listbox.takeItem(0) if typed or not self.typed_only: self.listbox.addItem(item) self._history.enqueue((item, typed)) # 'if typed:' to avoid clearing any partially entered command text if typed: self.listbox.clearSelection() self.listbox.setCurrentRow(len(self.history()) - 1) self.update_list() def button_clicked(self, label): session = self.controller.session if label == self.record_label: from chimerax.ui.open_save import SaveDialog if self._record_dialog is None: fmt = session.data_formats["ChimeraX commands"] self._record_dialog = dlg = SaveDialog(session, self.window.ui_area, "Save Commands", data_formats=[fmt]) from PyQt5.QtWidgets import QFrame, QLabel, QHBoxLayout, QVBoxLayout, QComboBox from PyQt5.QtWidgets import QCheckBox from PyQt5.QtCore import Qt options_frame = dlg.custom_area options_layout = QVBoxLayout(options_frame) options_frame.setLayout(options_layout) amount_frame = QFrame(options_frame) options_layout.addWidget(amount_frame, Qt.AlignCenter) amount_layout = QHBoxLayout(amount_frame) amount_layout.addWidget(QLabel("Save", amount_frame)) self.save_amount_widget = saw = QComboBox(amount_frame) saw.addItems(["all", "selected"]) amount_layout.addWidget(saw) amount_layout.addWidget(QLabel("commands", amount_frame)) amount_frame.setLayout(amount_layout) self.append_checkbox = QCheckBox("Append to file", options_frame) self.append_checkbox.stateChanged.connect(self.append_changed) options_layout.addWidget(self.append_checkbox, Qt.AlignCenter) self.overwrite_disclaimer = disclaimer = QLabel( "<small><i>(ignore overwrite warning)</i></small>", options_frame) options_layout.addWidget(disclaimer, Qt.AlignCenter) disclaimer.hide() else: dlg = self._record_dialog if not dlg.exec(): return path = dlg.selectedFiles()[0] if not path: from chimerax.core.errors import UserError raise UserError("No file specified for saving command history") if self.save_amount_widget.currentText() == "all": cmds = [cmd for cmd in self.history()] else: # listbox.selectedItems() may not be in order, so... items = [ self.listbox.item(i) for i in range(self.listbox.count()) if self.listbox.item(i).isSelected() ] cmds = [item.text() for item in items] from chimerax.io import open_output f = open_output(path, encoding='utf-8', append=self.append_checkbox.isChecked()) for cmd in cmds: print(cmd, file=f) f.close() return if label == self.execute_label: for item in self.listbox.selectedItems(): self.controller.cmd_replace(item.text()) self.controller.execute() return if label == "Delete": retain = [] listbox_index = 0 for h_item in self._history: if self.typed_only and not h_item[1]: retain.append(h_item) continue if not self.listbox.item(listbox_index).isSelected(): # not selected for deletion retain.append(h_item) listbox_index += 1 self._history.replace(retain) self.populate() return if label == "Copy": clipboard = session.ui.clipboard() clipboard.setText("\n".join( [item.text() for item in self.listbox.selectedItems()])) return if label == "Help": from chimerax.core.commands import run run(session, 'help help:user/tools/cli.html#history') return def down(self, shifted): sels = self.listbox.selectedIndexes() if len(sels) != 1: self._search_cache = (False, None) return sel = sels[0].row() orig_text = self.controller.text.currentText() match_against = None if shifted: was_searching, prev_search = self._search_cache if was_searching: match_against = prev_search else: words = orig_text.strip().split() if words: match_against = words[0] self._search_cache = (True, match_against) else: self._search_cache = (False, None) else: self._search_cache = (False, None) if match_against: last = self.listbox.count() - 1 while sel < last: if self.listbox.item(sel + 1).text().startswith(match_against): break sel += 1 if sel == self.listbox.count() - 1: return self.listbox.clearSelection() self.listbox.setCurrentRow(sel + 1) new_text = self.listbox.item(sel + 1).text() self.controller.cmd_replace(new_text) if orig_text == new_text: self.down(shifted) def fill_context_menu(self, menu, x, y): # avoid having actions destroyed when this routine returns # by stowing a reference in the menu itself from PyQt5.QtWidgets import QAction filter_action = QAction("Typed commands only", menu) filter_action.setCheckable(True) filter_action.setChecked(self.controller.settings.typed_only) filter_action.toggled.connect( lambda arg, f=self.controller._set_typed_only: f(arg)) menu.addAction(filter_action) def on_append_change(self, event): self.overwrite_disclaimer.Show(self.save_append_CheckBox.Value) def append_changed(self, append): if append: self.overwrite_disclaimer.show() else: self.overwrite_disclaimer.hide() def on_listbox(self, event): self.select() def populate(self): self.listbox.clear() history = self.history() self.listbox.addItems([cmd for cmd in history]) self.listbox.setCurrentRow(len(history) - 1) self.update_list() self.select() self.controller.text.lineEdit().setFocus() self.controller.text.lineEdit().selectAll() cursels = self.listbox.scrollToBottom() def search_reset(self): searching, target = self._search_cache if searching: self._search_cache = (False, None) self.listbox.blockSignals(True) self.listbox.clearSelection() self.listbox.setCurrentRow(self.listbox.count() - 1) self.listbox.blockSignals(False) def select(self): sels = self.listbox.selectedItems() if len(sels) != 1: return self.controller.cmd_replace(sels[0].text()) def up(self, shifted): sels = self.listbox.selectedIndexes() if len(sels) != 1: self._search_cache = (False, None) return sel = sels[0].row() orig_text = self.controller.text.currentText() match_against = None if shifted: was_searching, prev_search = self._search_cache if was_searching: match_against = prev_search else: words = orig_text.strip().split() if words: match_against = words[0] self._search_cache = (True, match_against) else: self._search_cache = (False, None) else: self._search_cache = (False, None) if match_against: while sel > 0: if self.listbox.item(sel - 1).text().startswith(match_against): break sel -= 1 if sel == 0: return self.listbox.clearSelection() self.listbox.setCurrentRow(sel - 1) new_text = self.listbox.item(sel - 1).text() self.controller.cmd_replace(new_text) if orig_text == new_text: self.up(shifted) def update_list(self): c = self.controller last8 = list(reversed(self.history()[-8:])) # without blocking signals, if the command list is empty then # "Command History" (the first entry) will execute... c.text.blockSignals(True) c.text.clear() c.text.addItems(last8 + [c.show_history_label, c.compact_label]) if not last8: c.text.lineEdit().setText("") c.text.blockSignals(False) def history(self): if self.typed_only: return [h[0] for h in self._history if h[1]] return [h[0] for h in self._history] def set_typed_only(self, typed_only): self.typed_only = typed_only self.populate() def _num_remembered_changed(self, new_hist_len): if len(self._history) > new_hist_len: self._history.replace(self._history[-new_hist_len:]) self.populate() self.controller.settings.num_remembered = new_hist_len
class MainWindow(QMainWindow): """ 主窗口类 """ def __init__(self): super().__init__() self.item_cnt = 0 #TODO self.setcolor = QColor(0, 0, 0) # 使用QListWidget来记录已有的图元,并用于选择图元。注:这是图元选择的简单实现方法,更好的实现是在画布中直接用鼠标选择图元 self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(200) # 使用QGraphicsView作为画布 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, 600, 600) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(600, 600) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget # 设置菜单栏 menubar = self.menuBar() file_menu = menubar.addMenu('文件') set_pen_act = file_menu.addAction('设置画笔') reset_canvas_act = file_menu.addAction('重置画布') exit_act = file_menu.addAction('退出') draw_menu = menubar.addMenu('绘制') line_menu = draw_menu.addMenu('线段') line_naive_act = line_menu.addAction('Naive') line_dda_act = line_menu.addAction('DDA') line_bresenham_act = line_menu.addAction('Bresenham') polygon_menu = draw_menu.addMenu('多边形') polygon_dda_act = polygon_menu.addAction('DDA') polygon_bresenham_act = polygon_menu.addAction('Bresenham') ellipse_act = draw_menu.addAction('椭圆') curve_menu = draw_menu.addMenu('曲线') curve_bezier_act = curve_menu.addAction('Bezier') curve_b_spline_act = curve_menu.addAction('B-spline') edit_menu = menubar.addMenu('编辑') translate_act = edit_menu.addAction('平移') rotate_act = edit_menu.addAction('旋转') scale_act = edit_menu.addAction('缩放') clip_menu = edit_menu.addMenu('裁剪') clip_cohen_sutherland_act = clip_menu.addAction('Cohen-Sutherland') clip_liang_barsky_act = clip_menu.addAction('Liang-Barsky') # 连接信号和槽函数 exit_act.triggered.connect(qApp.quit) line_naive_act.triggered.connect(self.line_naive_action) #TODO line_dda_act.triggered.connect(self.line_dda_action) line_bresenham_act.triggered.connect(self.line_bresenham_action) polygon_dda_act.triggered.connect(self.polygon_dda_action) polygon_bresenham_act.triggered.connect(self.polygon_bresenham_action) set_pen_act.triggered.connect(self.set_pen_action) reset_canvas_act.triggered.connect(self.reset_canvas_action) ellipse_act.triggered.connect(self.ellipse_action) curve_bezier_act.triggered.connect(self.curve_bezier_action) curve_b_spline_act.triggered.connect(self.curve_b_spline_action) translate_act.triggered.connect(self.translate_action) rotate_act.triggered.connect(self.rotate_action) scale_act.triggered.connect(self.scale_action) clip_cohen_sutherland_act.triggered.connect(self.clip_cohen_sutherland_action) clip_liang_barsky_act.triggered.connect(self.clip_liang_barsky_action) self.list_widget.currentTextChanged.connect(self.canvas_widget.selection_changed) # 设置主窗口的布局 self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.statusBar().showMessage('空闲') self.resize(600, 600) self.setWindowTitle('CG Demo') def get_id(self): _id = str(self.item_cnt) self.item_cnt += 1 return _id def line_naive_action(self): self.canvas_widget.start_draw_line('Naive', self.get_id()) self.statusBar().showMessage('Naive算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() #TODO def line_dda_action(self): self.canvas_widget.start_draw_line('DDA', self.get_id()) self.statusBar().showMessage('DDA算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_bresenham_action(self): self.canvas_widget.start_draw_line('Bresenham', self.get_id()) self.statusBar().showMessage('Bresenham算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_dda_action(self): self.canvas_widget.start_draw_polygon('DDA', self.get_id()) self.statusBar().showMessage('Bresenham算法绘制polygon') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_bresenham_action(self): self.canvas_widget.start_draw_polygon('Bresenham', self.get_id()) self.statusBar().showMessage('Bresenham算法绘制polygon') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def set_pen_action(self): color = QColorDialog.getColor() self.setcolor = color self.statusBar().showMessage('设置画笔') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def reset_canvas_action(self): self.item_cnt = 0 dialog = QDialog() dialog.setWindowTitle('重置画布') dialog.resize(400,247) height, width = 0, 0 errorFlag = 0 while 1: if ((height < 100 or width < 100 or height > 1000 or width > 1000) and (errorFlag == 0)): height, heightPressed = QInputDialog.getInt(self, '重置画布', 'Please input height(range:100-1000):', QLineEdit.Normal) width, widthPressed = QInputDialog.getInt(self, '重置画布', 'Please input width(range:100-1000):', QLineEdit.Normal) errorFlag = 1 elif ((height < 100 or width < 100 or height > 1000 or width > 1000) and (errorFlag == 1)): QMessageBox.critical(self, 'Error', 'out of range') errorFlag = 0 else: break if heightPressed and height and widthPressed and width: #clear item self.canvas_widget.clearItem() self.list_widget.clearSelection() self.canvas_widget.clear_selection() #resetsize self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, width, height) self.canvas_widget.resize(width, height) self.canvas_widget.setFixedSize(width, height) self.statusBar().showMessage('空闲') self.setMaximumSize(width, height) self.resize(width, height) def ellipse_action(self): self.canvas_widget.start_draw_ellipse(self.get_id()) self.statusBar().showMessage('椭圆') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_bezier_action(self): self.canvas_widget.start_draw_curve('Bezier', self.get_id(), 3)#numofpoint self.statusBar().showMessage('Bezier算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_b_spline_action(self): self.canvas_widget.start_draw_curve('B-spline', self.get_id(), 4)#numofpoint self.statusBar().showMessage('B-spline算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def translate_action(self): self.canvas_widget.start_translate() self.statusBar().showMessage('平移') #self.list_widget.clearSelection() #self.canvas_widget.clear_selection() def rotate_action(self): self.canvas_widget.start_rotate() self.statusBar().showMessage('旋转') #self.list_widget.clearSelection() #self.canvas_widget.clear_selection() def scale_action(self): self.canvas_widget.start_scale() self.statusBar().showMessage('缩放') #self.list_widget.clearSelection() #self.canvas_widget.clear_selection() def clip_cohen_sutherland_action(self): self.canvas_widget.start_clip('Cohen-Sutherland') self.statusBar().showMessage('Cohen-Sutherland算法裁剪') #self.list_widget.clearSelection() #self.canvas_widget.clear_selection() def clip_liang_barsky_action(self): self.canvas_widget.start_clip('Liang-Barsky') self.statusBar().showMessage('Liang-Barsky算法裁剪')
class MainWindow(QMainWindow): """ 主窗口类 """ def __init__(self): super().__init__() self.item_cnt = 0 # 使用QListWidget来记录已有的图元,并用于选择图元。注:这是图元选择的简单实现方法,更好的实现是在画布中直接用鼠标选择图元 self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(200) # 使用QGraphicsView作为画布 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, 600, 600) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(600, 600) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget #confirm button self.btn = QPushButton('OK', self) self.btn.setFixedSize(40, 30) # 设置菜单栏 menubar = self.menuBar() file_menu = menubar.addMenu('文件') set_pen_act = file_menu.addAction('设置画笔') reset_canvas_act = file_menu.addAction('重置画布') save_canvas_act = file_menu.addAction('保存画布') exit_act = file_menu.addAction('退出') draw_menu = menubar.addMenu('绘制') line_menu = draw_menu.addMenu('线段') line_naive_act = line_menu.addAction('Naive') line_dda_act = line_menu.addAction('DDA') line_bresenham_act = line_menu.addAction('Bresenham') polygon_menu = draw_menu.addMenu('多边形') polygon_dda_act = polygon_menu.addAction('DDA') polygon_bresenham_act = polygon_menu.addAction('Bresenham') ellipse_act = draw_menu.addAction('椭圆') curve_menu = draw_menu.addMenu('曲线') curve_bezier_act = curve_menu.addAction('Bezier') curve_b_spline_act = curve_menu.addAction('B-spline') edit_menu = menubar.addMenu('编辑') translate_act = edit_menu.addAction('平移') rotate_act = edit_menu.addAction('旋转') scale_act = edit_menu.addAction('缩放') clip_menu = edit_menu.addMenu('裁剪') clip_cohen_sutherland_act = clip_menu.addAction('Cohen-Sutherland') clip_liang_barsky_act = clip_menu.addAction('Liang-Barsky') addition_func_menu = menubar.addMenu('附加功能') select_item_act = addition_func_menu.addAction('选择图元') polygon_addition_menu = addition_func_menu.addMenu('多边形') polygon_fill_act = polygon_addition_menu.addAction('多边形填充') polygon_clip_act = polygon_addition_menu.addAction('多边形裁剪') copy_act = addition_func_menu.addAction('复制粘贴') undo_act = addition_func_menu.addAction('撤销') delete_act = addition_func_menu.addAction('删除') # 连接信号和槽函数 exit_act.triggered.connect(qApp.quit) set_pen_act.triggered.connect(self.set_pen_action) reset_canvas_act.triggered.connect(self.reset_canvas_action) save_canvas_act.triggered.connect(self.save_canvas_action) line_naive_act.triggered.connect(self.line_naive_action) line_dda_act.triggered.connect(self.line_dda_action) line_bresenham_act.triggered.connect(self.line_bresenham_action) polygon_dda_act.triggered.connect(self.polygon_dda_action) polygon_bresenham_act.triggered.connect(self.polygon_bresenham_action) ellipse_act.triggered.connect(self.ellipse_action) curve_bezier_act.triggered.connect(self.curve_bezier_action) curve_b_spline_act.triggered.connect(self.curve_b_spline_action) translate_act.triggered.connect(self.translate_action) rotate_act.triggered.connect(self.rotate_action) scale_act.triggered.connect(self.scale_action) clip_cohen_sutherland_act.triggered.connect(self.clip_cohen_sutherland_action) clip_liang_barsky_act.triggered.connect(self.clip_liang_barsky_action) self.list_widget.currentTextChanged.connect(self.canvas_widget.selection_changed) self.btn.clicked.connect(self.on_click) select_item_act.triggered.connect(self.select_item_action) polygon_fill_act.triggered.connect(self.polygon_fill_action) polygon_clip_act.triggered.connect(self.polygon_clip_action) copy_act.triggered.connect(self.copy_action) undo_act.triggered.connect(self.undo_action) delete_act.triggered.connect(self.delete_action) # 设置主窗口的布局 self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) #TODO: move button to down below self.hbox_layout.addWidget(self.btn) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.statusBar().showMessage('空闲') self.resize(600, 600) self.setWindowTitle('CG Demo') def reset_id(self): self.item_cnt = 0 def get_id(self): self.item_cnt += 1 _id = str(self.item_cnt) return _id def get_present_id(self): _id = str(self.item_cnt) return _id def set_pen_action(self): col = QColorDialog.getColor() if col.isValid(): self.canvas_widget.start_change_color(col) def reset_canvas_action(self): self.canvas_widget.start_reset_canvas() self.list_widget.clearSelection() self.canvas_widget.clear_selection() dialog = QDialog() form = QFormLayout(dialog) box1 = QSpinBox(dialog) box1.setRange(100, 1000) box2 = QSpinBox(dialog) box2.setRange(100, 1000) form.addRow('Width:', box1) form.addRow('Height:', box2) buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttonBox.accepted.connect(dialog.accept) buttonBox.rejected.connect(dialog.reject) form.addRow(buttonBox) if dialog.exec(): self.w = box1.value() self.h = box2.value() self.scene.setSceneRect(0, 0, self.w, self.h) self.canvas_widget.resize(self.w,self.h) self.canvas_widget.setFixedSize(self.w, self.h) self.statusBar().showMessage('空闲') self.setMaximumHeight(self.h) self.setMaximumWidth(self.w) self.resize(self.w, self.h) def save_canvas_action(self): self.canvas_widget.start_save_canvas() def line_naive_action(self): self.canvas_widget.start_draw_line('Naive', self.get_present_id()) self.statusBar().showMessage('Naive算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_dda_action(self): self.canvas_widget.start_draw_line('DDA', self.get_present_id()) self.statusBar().showMessage('DDA算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_bresenham_action(self): self.canvas_widget.start_draw_line('Bresenham', self.get_present_id()) self.statusBar().showMessage('Bresenham算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_dda_action(self): self.canvas_widget.start_draw_polygon('DDA', self.get_present_id()) self.statusBar().showMessage('DDA算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_bresenham_action(self): self.canvas_widget.start_draw_polygon('Bresenham', self.get_present_id()) self.statusBar().showMessage('Bresenham算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def ellipse_action(self): self.canvas_widget.start_draw_ellipse(self.get_present_id()) self.statusBar().showMessage('绘制椭圆') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_bezier_action(self): self.canvas_widget.start_draw_curve('Bezier', self.get_present_id()) self.statusBar().showMessage('Bezier算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_b_spline_action(self): self.canvas_widget.start_draw_curve('B-spline', self.get_present_id()) self.statusBar().showMessage('B-spline算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def translate_action(self): self.canvas_widget.start_translate() self.statusBar().showMessage('图元平移') def rotate_action(self): self.canvas_widget.start_rotate() self.statusBar().showMessage('图元旋转') def scale_action(self): self.canvas_widget.start_scale() self.statusBar().showMessage('图元缩放') def clip_cohen_sutherland_action(self): self.canvas_widget.start_clip('Cohen-Sutherland') self.statusBar().showMessage('Cohen-Sutherland算法对线段裁剪') def clip_liang_barsky_action(self): self.canvas_widget.start_clip('Liang-Barsky') self.statusBar().showMessage('Liang-Barsky算法对线段裁剪') def on_click(self): self.canvas_widget.finish_draw_curve(self.get_present_id()) def select_item_action(self): self.canvas_widget.select_item() self.statusBar().showMessage('鼠标点击选择图元') def polygon_fill_action(self): self.canvas_widget.start_polygon_fill() self.statusBar().showMessage('多边形填充') def polygon_clip_action(self): self.canvas_widget.start_polygon_clip() self.statusBar().showMessage('多边形裁剪') def copy_action(self): self.canvas_widget.start_copy() self.statusBar().showMessage('图元复制粘贴') def undo_action(self): self.canvas_widget.start_undo() self.statusBar().showMessage('撤销操作') def delete_action(self): self.canvas_widget.start_delete() self.statusBar().showMessage('删除图元')
class ArtToolWindow(QMainWindow): # -------------------------------------------------- # CONSTRUCTOR # -------------------------------------------------- def __init__(self, *args): super(ArtToolWindow, self).__init__(*args) # State self.metadata = None self.comboTabIdx = -1 self.currentFbx = -1 self.currentCombo = -1 self.ignoreSVN = 0 self.additionsClipboard = None self.cancelledSVN = False self.editPane = None self.currentFilter = None self.lastSVNCheck = 0 self.dialogUp = False self.mainLayout = None # Load our config self.config = createGetConfig() # Left Pane leftPane = QWidget() leftLayout = QVBoxLayout(leftPane) # Streamlabs logo slabslogo = QLabel() slabslogo.setPixmap(QPixmap("arttool/streamlabs.png")) slabslogo.setScaledContents(True) slabslogo.show() leftLayout.addWidget(slabslogo) slabslogo.setMaximumWidth(300) slabslogo.setMaximumHeight(53) # Filter box self.fbxfilter = QLineEdit() self.fbxfilter.editingFinished.connect(lambda: self.onFbxFilterChanged()) leftLayout.addWidget(self.fbxfilter) # make a list widget for fbx's self.fbxlist = QListWidget() self.fbxlist.itemSelectionChanged.connect(lambda: self.onFbxClicked()) self.fbxlist.setMinimumHeight(640) # make the combo tab combotab = QWidget() combotab.setAutoFillBackground(True) # combo buttons b = QPushButton("Add") b.setParent(combotab) b.setGeometry(5,5, 75, 30) b.pressed.connect(lambda: self.onAddCombo()) b = QPushButton("Del") b.setParent(combotab) b.setGeometry(85, 5, 75, 30) b.pressed.connect(lambda: self.onDelCombo()) # make alist widget for combos self.combolist = QListWidget() self.combolist.itemSelectionChanged.connect(lambda: self.onComboClicked()) self.combolist.setParent(combotab) self.combolist.setGeometry(0, 40, 294, 600) # make fbx/combo tabs tabs = QTabWidget() tabs.currentChanged.connect(lambda i: self.onComboTabChanged(i)) tabs.setGeometry(0,0,300, 640) tabs.addTab(self.fbxlist, "FBX") tabs.addTab(combotab, "COMBOS") leftLayout.addWidget(tabs) # top splitter topSplitter = QSplitter(Qt.Horizontal) topSplitter.addWidget(leftPane) # Layout for edit pane rightPane = QWidget() self.mainLayout = QHBoxLayout(rightPane) topSplitter.addWidget(rightPane) # Fill in files lists self.fillFbxList() self.fillComboList() # crate main splitter mainSplitter = QSplitter(Qt.Vertical) mainSplitter.addWidget(topSplitter) # bottom pane bottomPane = QWidget() bottomArea = QHBoxLayout(bottomPane) # output window self.outputWindow = QTextEdit() self.outputWindow.setMinimumHeight(90) bottomArea.addWidget(self.outputWindow) # buttons area buttonArea = QWidget() buttonArea.setMinimumWidth(150) buttonArea.setMinimumHeight(60) locs = [(0, 0), (0, 30), (0, 60), (75, 0), (75, 30), (75, 60)] c = 0 for nn in ["Refresh", "Autobuild", "Rebuild All", "S3 Upload", "XLS Export", "Release Masks"]: b = QPushButton(nn) b.setParent(buttonArea) (x, y) = locs[c] c += 1 b.setGeometry(x, y, 75, 30) b.pressed.connect(lambda nn=nn: self.onMainButton(nn)) bottomArea.addWidget(buttonArea) mainSplitter.addWidget(bottomPane) # Show the window self.setCentralWidget(mainSplitter) self.setGeometry(self.config["x"], self.config["y"], 1024, 900) self.setWindowTitle('Streamlabs Art Tool') self.setWindowIcon(QIcon('arttool/arttoolicon.png')) # Check our binaries self.checkBinaries() # -------------------------------------------------- # Fill(refill) in the fbx list # -------------------------------------------------- def fillFbxList(self): # Get list of fbx files self.fbxfiles = getFbxFileList(".") # Clear list while self.fbxlist.count() > 0: i = self.fbxlist.takeItem(0) i = None # Get filter filt = self.fbxfilter.text().lower() if not filt or len(filt) < 1: filt = None # Fill list self.fbxlistMap = dict() self.fbxlistRevMap = dict() fbxidx = 0 listidx = 0 for fbx in self.fbxfiles: if not filt or filt in fbx.lower(): self.fbxlist.addItem(fbx[2:]) self.fbxlistMap[listidx] = fbxidx self.fbxlistRevMap[fbxidx] = listidx listidx += 1 fbxidx += 1 # color fbxlist items for idx in range(0, len(self.fbxfiles)): self.setFbxColorIcon(idx) self.resetEditPane() # -------------------------------------------------- # Fill(refill) in the combos list # -------------------------------------------------- def fillComboList(self): # Get list of fbx files self.combofiles = getComboFileList(".") # Clear list while self.combolist.count() > 0: i = self.combolist.takeItem(0) i = None # Fill list for combo in self.combofiles: self.combolist.addItem(combo[2:]) idx = self.combolist.count() - 1 # color list items for idx in range(0, len(self.combofiles)): self.setComboColorIcon(idx) self.resetEditPane() def resetEditPane(self): self.currentCombo = -1 self.currentFbx = -1 self.metadata = None self.fbxlist.clearSelection() self.combolist.clearSelection() if self.mainLayout is not None: self.createMaskEditPane(None) # -------------------------------------------------- # Check binaries # -------------------------------------------------- def checkBinaries(self): gotSVN = os.path.exists(SVNBIN.replace('"', '')) gotMM = os.path.exists(MASKMAKERBIN) gotRP = os.path.exists(MORPHRESTFILE) if not gotSVN: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("You seem to be missing " + os.path.basename(SVNBIN)) msg.setInformativeText("You should (re)install tortoiseSVN, and be sure to install the command line tools.") msg.setWindowTitle("Missing Binary File") msg.setStandardButtons(QMessageBox.Ok) self.ignoreSVN += 1 self.dialogUp = True msg.exec_() self.dialogUp = False if not gotMM: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("You seem to be missing " + os.path.basename(MASKMAKERBIN)) msg.setWindowTitle("Missing Binary File") msg.setStandardButtons(QMessageBox.Ok) self.ignoreSVN += 1 self.dialogUp = True msg.exec_() self.dialogUp = False if not gotRP: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("You seem to be missing " + os.path.basename(MORPHRESTFILE)) msg.setWindowTitle("Missing Binary File") msg.setStandardButtons(QMessageBox.Ok) self.ignoreSVN += 1 self.dialogUp = True msg.exec_() self.dialogUp = False # -------------------------------------------------- # Create generic widget # -------------------------------------------------- def createLabelWidget(self, parent, name, field, x, y, noedit=False): q = QLabel(name) q.setParent(parent) q.setGeometry(x, y, FIELD_WIDTH - 10, 26) q.setFont(QFont("Arial", 12, QFont.Bold)) q.show() if field == None: return q critical = critical_mask if self.metadata["fbx"].lower().endswith(".json"): critical = critical_combo value = self.metadata[field] if field in DROP_DOWNS: dropvals = DROP_DOWNS[field] q = QComboBox() idx = 0 selidx = -1 for s in dropvals: if s == str(value): selidx = idx q.addItem(s) idx += 1 if selidx < 0: selidx = 0 q.setCurrentIndex(selidx) q.currentIndexChanged.connect(lambda state: self.onDropdownChanged(state, field)) elif type(value) is str or type(value) is float or type(value) is int: if noedit: q = QLabel(str(value)) else: q = QLineEdit(str(value)) q.textChanged.connect(lambda text: self.onTextFieldChanged(text, field)) if critical(field) and len(value) == 0: q.setStyleSheet("border: 1px solid #FF0000;") elif desired(field) and len(value) == 0: q.setStyleSheet("border: 1px solid #FF7F50;") else: q.setStyleSheet("border: 0px;") elif type(value) is bool: q = QCheckBox() q.setChecked(value) q.stateChanged.connect(lambda state: self.onCheckboxChanged(state, field)) q.setParent(parent) if type(value) is int: q.setGeometry(x + FIELD_WIDTH - 10, y, FIELD_WIDTH, 20) else: q.setGeometry(x + FIELD_WIDTH - 10, y, PANE_WIDTH - FIELD_WIDTH - 10, 20) q.setFont(QFont("Arial", 12, QFont.Bold)) q.show() return q # -------------------------------------------------- # Colors and Icons for main FBX list # -------------------------------------------------- def setFbxColorIconInternal(self, mdc, mt, nb, idx): self.fbxlist.item(idx).setFont(QFont("Arial", 12, QFont.Bold)) if mdc == CHECKMETA_GOOD: self.fbxlist.item(idx).setForeground(QBrush(QColor("#32CD32"))) elif mdc == CHECKMETA_ERROR: self.fbxlist.item(idx).setForeground(QBrush(QColor("#FF0000"))) elif mdc == CHECKMETA_WARNING: self.fbxlist.item(idx).setForeground(QBrush(QColor("#FF7F50"))) elif mdc == CHECKMETA_NORELEASE: self.fbxlist.item(idx).setForeground(QBrush(QColor("#000000"))) elif mdc == CHECKMETA_WITHPLUGIN: self.fbxlist.item(idx).setForeground(QBrush(QColor("#5070FF"))) if mt == MASK_UNKNOWN: self.fbxlist.item(idx).setIcon(QIcon("arttool/unknownicon.png")) else: if nb: if mt == MASK_NORMAL: self.fbxlist.item(idx).setIcon(QIcon("arttool/maskicon_build.png")) elif mt == MASK_MORPH: self.fbxlist.item(idx).setIcon(QIcon("arttool/morphicon_build.png")) else: if mt == MASK_NORMAL: self.fbxlist.item(idx).setIcon(QIcon("arttool/maskicon.png")) elif mt == MASK_MORPH: self.fbxlist.item(idx).setIcon(QIcon("arttool/morphicon.png")) def setFbxColorIcon(self, idx): if idx in self.fbxlistRevMap: mdc, mt = checkMetaDataFile(self.fbxfiles[idx]) nb = doesFileNeedRebuilding(self.fbxfiles[idx]) self.setFbxColorIconInternal(mdc, mt, nb, self.fbxlistRevMap[idx]) def setComboColorIconInternal(self, mdc, mt, nb, idx): self.combolist.item(idx).setFont(QFont("Arial", 12, QFont.Bold)) if mdc == CHECKMETA_GOOD: self.combolist.item(idx).setForeground(QBrush(QColor("#32CD32"))) elif mdc == CHECKMETA_ERROR: self.combolist.item(idx).setForeground(QBrush(QColor("#FF0000"))) elif mdc == CHECKMETA_WARNING: self.combolist.item(idx).setForeground(QBrush(QColor("#FF7F50"))) elif mdc == CHECKMETA_NORELEASE: self.combolist.item(idx).setForeground(QBrush(QColor("#000000"))) elif mdc == CHECKMETA_WITHPLUGIN: self.combolist.item(idx).setForeground(QBrush(QColor("#5070FF"))) if nb: self.combolist.item(idx).setIcon(QIcon("arttool/comboicon_build.png")) else: self.combolist.item(idx).setIcon(QIcon("arttool/comboicon.png")) def setComboColorIcon(self, idx): mdc, mt = checkMetaDataFile(self.combofiles[idx]) nb = doesFileNeedRebuilding(self.combofiles[idx]) self.setComboColorIconInternal(mdc, mt, nb, idx) def updateListColorIcon(self): mdc, mt = checkMetaData(self.metadata) nb = doesFileNeedRebuilding(self.metadata["fbx"], self.metadata) if self.comboTabIdx == 0: self.setFbxColorIconInternal(mdc, mt, nb, self.fbxlistRevMap[self.currentFbx]) else: self.setComboColorIconInternal(mdc, mt, nb, self.currentCombo) # -------------------------------------------------- # createMaskEditPane # -------------------------------------------------- def createMaskEditPane(self, fbxfile): if self.editPane: self.mainLayout.removeWidget(self.editPane) self.editPane.deleteLater() self.editPane = QWidget() self.mainLayout.addWidget(self.editPane) self.editPane.setMinimumWidth(PANE_WIDTH) self.editPane.show() # empty pane if fbxfile == None: return # mask icon png q = QLabel() q.setParent(self.editPane) pf = os.path.abspath(fbxfile.lower().replace(".fbx", ".gif").replace(".json", ".gif")) if os.path.exists(pf): m = QMovie(pf) else: m = QMovie("arttool/noicon.png") q.setMovie(m) m.start() q.setScaledContents(True) q.setGeometry(0, 2, 256, 256) q.show() # mask file name q = QLabel(fbxfile[2:]) q.setParent(self.editPane) q.setGeometry(260, 44, 600, 36) q.setFont(QFont("Arial", 14, QFont.Bold)) q.show() # uuid q = QLabel(self.metadata["uuid"]) q.setParent(self.editPane) q.setGeometry(260, 80, 600, 20) q.setStyleSheet("font: 10pt;") # q.setFont(QFont( "Arial", 6)) q.show() # buttons b = QPushButton("BUILD") b.setParent(self.editPane) b.setGeometry(260, 2, 64, 32) q.setFont(QFont("Arial", 14, QFont.Bold)) b.pressed.connect(lambda: self.onBuild()) b.show() # Tabbed Panel tabs = QTabWidget(self.editPane) tabs.setGeometry(0, 264, PANE_WIDTH, 600) # mask meta data fields tab1 = QWidget() y = 2 dy = 28 self.paneWidgets = dict() for field in UI_FIELDS[self.comboTabIdx]: if field != "uuid": w = self.createLabelWidget(tab1, MASK_UI_NAMES[field], field, 10, y) self.paneWidgets[field] = w if field in MASK_FIELD_TOOLTIPS: w.setToolTip(MASK_FIELD_TOOLTIPS[field]) y += dy tab1.setAutoFillBackground(True) tabs.addTab(tab1, "Mask Meta Data") if self.comboTabIdx == 0: tab2 = self.createAdditionsTab() tabs.addTab(tab2, "Additions") else: tab2 = self.createCombosTab() tabs.addTab(tab2, "Combinations") tabs.show() def createCombosTab(self): # combos tab tab2 = QWidget() y = 10 dy = 40 self.createLabelWidget(tab2, "Combo Files", None, 10, y) y += dy self.comboWidgets = list() for cidx in range(0,10): q = QComboBox() idx = 1 selidx = -1 value = self.metadata["additions"][cidx] q.addItem(" None ") for f in self.fbxfiles: if f == str(value): selidx = idx q.addItem(f[2:]) idx += 1 if selidx < 0: selidx = 0 q.setCurrentIndex(selidx) q.setParent(tab2) q.setGeometry(10, y, 600, 30) y += dy q.setFont(QFont("Arial", 12, QFont.Bold)) q.show() q.currentIndexChanged.connect(lambda state, cidx=cidx: self.onComboFileChanged(state,cidx)) self.comboWidgets.append(q) tab2.setAutoFillBackground(True) return tab2 def createAdditionsTab(self): # additions tab tab2 = QWidget() y = 10 dy = 40 self.createLabelWidget(tab2, "Mask Build Additions", None, 10, y) y += dy # make a list widget self.addslist = QListWidget() if "additions" in self.metadata: additions = self.metadata["additions"] idx = 0 for addition in additions: self.addslist.addItem(addition["type"] + " : " + addition["name"]) self.addslist.item(idx).setFont(QFont("Arial", 12, QFont.Bold)) idx += 1 self.addslist.itemDoubleClicked.connect(lambda: self.onEditAddition()) self.addslist.setParent(tab2) self.addslist.setGeometry(10, y, PANE_WIDTH - 20, 250) y += 260 # bottom buttons for additons x = 10 b = QPushButton("Add") b.setParent(tab2) b.setGeometry(x, y, 75, 30) b.pressed.connect(lambda: self.onAddAddition()) x += 85 b = QPushButton("Edit") b.setParent(tab2) b.setGeometry(x, y, 75, 30) b.pressed.connect(lambda: self.onEditAddition()) x += 85 b = QPushButton("Del") b.setParent(tab2) b.setGeometry(x, y, 75, 30) b.pressed.connect(lambda: self.onDelAddition()) x += 85 b = QPushButton("Dupe") b.setParent(tab2) b.setGeometry(x, y, 75, 30) b.pressed.connect(lambda: self.onCopyAddition()) x += 85 b = QPushButton("Up") b.setParent(tab2) b.setGeometry(x, y, 75, 30) b.pressed.connect(lambda: self.onMoveUpAddition()) x += 85 b = QPushButton("Down") b.setParent(tab2) b.setGeometry(x, y, 75, 30) b.pressed.connect(lambda: self.onMoveDownAddition()) # top buttons for additions x = 300 b = QPushButton("COPY ALL") b.setParent(tab2) b.setGeometry(x, 10, 75, 30) b.pressed.connect(lambda: self.onCopyAllAdditions()) x += 85 b = QPushButton("PASTE ALL") b.setParent(tab2) b.setGeometry(x, 10, 75, 30) b.pressed.connect(lambda: self.onPasteAllAdditions()) tab2.setAutoFillBackground(True) return tab2 # -------------------------------------------------- # saveCurrentMetadata # -------------------------------------------------- def saveCurrentMetadata(self): if self.metadata: fbxfile = None if self.currentFbx >= 0: fbxfile = self.fbxfiles[self.currentFbx] elif self.currentCombo >= 0: fbxfile = self.combofiles[self.currentCombo] if fbxfile: metafile = getMetaFileName(fbxfile) oldmetadata = loadMetadataFile(fbxfile) if oldmetadata != self.metadata: writeMetaData(metafile, self.metadata, True) print("saving", metafile) # -------------------------------------------------- # WIDGET SIGNALS CALLBACKS # -------------------------------------------------- # Main buttons def onMainButton(self, button): if button == "S3 Upload": self.uploadToS3() elif button == "Refresh": self.fillFbxList() self.fillComboList() elif button == "Autobuild": self.doAutobuild() elif button == "Rebuild All": self.doRebuildAll() elif button == "XLS Export": self.onWriteMetadataExcel() elif button == "Release Masks": self.doReleaseMasks() # TODO : hook up to a button def onMakeThumbsMovie(self): # self.ignoreSVN += 1 # self.dialogUp = True # addn = ReleasesDialog.go_modal(self) # self.dialogUp = False # quick hack for fun framenum = 1 for fbx in self.fbxfiles: src = os.path.abspath(fbx.replace(".fbx", ".png").replace(".FBX", ".png")) # dst = "c:\\Temp\\masks\\frame%04d.png" % framenum dst = "c:\\Temp\\masks\\" + os.path.basename(src) fileOkay = True if "frogHead" in src: fileOkay = False if "joshog" in src: fileOkay = False if os.path.exists(src) and fileOkay: print("copy", src, dst) framenum += 1 copyfile(src, dst) def onComboTabChanged(self, tab): self.comboTabIdx = tab self.resetEditPane() def onComboFileChanged(self, state, which): if state == 0: self.metadata["additions"][which] = "" else: self.metadata["additions"][which] = self.fbxfiles[state - 1] # FBX file clicked in list def onFbxClicked(self): if self.comboTabIdx != 0: return k = self.fbxlist.currentRow() if k >= 0 and k < self.fbxlist.count(): self.saveCurrentMetadata() self.currentCombo = -1 self.currentFbx = self.fbxlistMap[k] fbxfile = self.fbxfiles[self.currentFbx] self.metadata = createGetMetaData(fbxfile) self.updateListColorIcon() self.createMaskEditPane(fbxfile) # FBX Filter box changed def onFbxFilterChanged(self): filt = self.fbxfilter.text().lower() if not filt or len(filt) < 1: filt = None if filt != self.currentFilter: self.fillFbxList() self.currentFilter = filt # FBX file clicked in list def onComboClicked(self): if self.comboTabIdx != 1: return k = self.combolist.currentRow() if k >= 0 and k < self.combolist.count(): self.saveCurrentMetadata() self.currentCombo = k self.currentFbx = -1 combofile = self.combofiles[self.currentCombo] self.metadata = createGetMetaData(combofile) self.updateListColorIcon() self.createMaskEditPane(combofile) def onAddCombo(self): file, filter = QFileDialog.getSaveFileName(self, 'Save file', os.path.abspath("."), "Mask files (*.json)") if file is not None and len(file) > 0: combofile = make_path_relative(os.path.abspath("."), os.path.abspath(file)) print("adding combo", combofile) self.saveCurrentMetadata() metadata = createGetMetaData(combofile) self.fillComboList() for idx in range(0, len(self.combofiles)): if combofile == self.combofiles[idx]: self.currentCombo = idx break if self.currentCombo >= 0: self.combolist.setCurrentRow(self.currentCombo) def onDelCombo(self): pass # text field changed def onTextFieldChanged(self, text, field): if type(self.metadata[field]) is int: self.metadata[field] = int(text) elif type(self.metadata[field]) is float: self.metadata[field] = float(text) else: self.metadata[field] = text critical = critical_mask if self.metadata["fbx"].lower().endswith(".json"): critical = critical_combo if critical(field) and len(text) == 0: self.paneWidgets[field].setStyleSheet("border: 1px solid #FF0000;") elif desired(field) and len(text) == 0: self.paneWidgets[field].setStyleSheet("border: 1px solid #FF7F50;") else: self.paneWidgets[field].setStyleSheet("border: 0px;") self.updateListColorIcon() # checkbox changed def onCheckboxChanged(self, state, field): if state == 0: self.metadata[field] = False else: self.metadata[field] = True self.updateListColorIcon() # texsize changed def onDropdownChanged(self, state, field): if type(self.metadata[field]) is int: self.metadata[field] = int(DROP_DOWNS[field][state]) elif type(self.metadata[field]) is float: self.metadata[field] = float(DROP_DOWNS[field][state]) else: self.metadata[field] = DROP_DOWNS[field][state] # called before exit def finalCleanup(self): self.cancelledSVN = True self.saveCurrentMetadata() # This is just getting annoying # # QApplication.setOverrideCursor(Qt.WaitCursor) #needcommit = svnNeedsCommit() # QApplication.restoreOverrideCursor() #if needcommit: # msg = QMessageBox() # msg.setIcon(QMessageBox.Warning) # msg.setText("You have changed files in your depot.") # msg.setInformativeText("Be sure to commit your changes to avoid conflicts.") # msg.setWindowTitle("Files Changed - SVN Commit Recommended") # msg.setStandardButtons(QMessageBox.Ok) # self.ignoreSVN += 1 # self.dialogUp = True # msg.exec_() # self.dialogUp = False # # WRITE CONFIG # geo = self.geometry() self.config["x"] = geo.x() self.config["y"] = geo.y() writeMetaData(getConfigFile(), self.config) # build def onBuild(self): QApplication.setOverrideCursor(Qt.WaitCursor) if self.currentCombo >= 0: # build combo combofile = self.combofiles[self.currentCombo] buildCombo(combofile, self.outputWindow, self.metadata) filetype = "Combo Json" else: # build mask fbxfile = self.fbxfiles[self.currentFbx] svnAddFile(fbxfile) buildMask(fbxfile, self.outputWindow, self.metadata) filetype = "FBX" deps, missing = getDependencies(self.metadata) for d in deps: svnAddFile(d["file"]) for m in missing: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("This " + filetype +" depends on " + m + ", which cannot be found.") msg.setWindowTitle("Missing PNG File") msg.setStandardButtons(QMessageBox.Ok) self.ignoreSVN += 1 self.dialogUp = True msg.exec_() self.dialogUp = False QApplication.restoreOverrideCursor() self.updateListColorIcon() def onAddAddition(self): self.ignoreSVN += 1 self.dialogUp = True addn = NewAdditionDialog.go_modal(self) self.dialogUp = False if addn: if "additions" not in self.metadata: self.metadata["additions"] = list() self.metadata["additions"].append(addn) idx = self.addslist.count() self.addslist.addItem(addn["type"] + " : " + addn["name"]) self.addslist.item(idx).setFont(QFont("Arial", 12, QFont.Bold)) def onEditAddition(self): idx = self.addslist.currentRow() if idx >= 0: self.ignoreSVN += 1 self.dialogUp = True addn = AdditionDialog.go_modal(self, self.metadata["additions"][idx]) self.dialogUp = False if addn: self.addslist.item(idx).setText(addn["type"] + " : " + addn["name"]) self.metadata["additions"][idx] = addn def onDelAddition(self): idx = self.addslist.currentRow() if idx >= 0: del self.metadata["additions"][idx] i = self.addslist.takeItem(idx) i = None def onCopyAddition(self): idx = self.addslist.currentRow() if idx >= 0: addn = deepcopy(self.metadata["additions"][idx]) self.addslist.insertItem(idx + 1, addn["type"] + " : " + addn["name"]) self.addslist.item(idx + 1).setFont(QFont("Arial", 12, QFont.Bold)) self.addslist.setCurrentRow(idx + 1) self.metadata["additions"].insert(idx + 1, addn) def onMoveUpAddition(self): idx = self.addslist.currentRow() if idx > 0: self.addslist.insertItem(idx - 1, self.addslist.takeItem(idx)) self.addslist.setCurrentRow(idx - 1) self.metadata["additions"].insert(idx - 1, self.metadata["additions"].pop(idx)) def onMoveDownAddition(self): idx = self.addslist.currentRow() if idx >= 0 and self.addslist.count() > 1 and idx < (self.addslist.count() - 1): self.addslist.insertItem(idx + 1, self.addslist.takeItem(idx)) self.addslist.setCurrentRow(idx + 1) self.metadata["additions"].insert(idx + 1, self.metadata["additions"].pop(idx)) def onCopyAllAdditions(self): self.additionsClipboard = deepcopy(self.metadata["additions"]) def onPasteAllAdditions(self): if self.additionsClipboard: self.metadata["additions"].extend(deepcopy(self.additionsClipboard)) for addn in self.additionsClipboard: idx = self.addslist.count() self.addslist.addItem(addn["type"] + " : " + addn["name"]) self.addslist.item(idx).setFont(QFont("Arial", 12, QFont.Bold)) def onFocusChanged(self, old, now): if self.cancelledSVN or self.dialogUp: return if not old and now: if self.ignoreSVN > 0: self.ignoreSVN -= 1 return if (time.time() - self.lastSVNCheck) < SVN_CHECK_TIME: return QApplication.setOverrideCursor(Qt.WaitCursor) needsup = svnNeedsUpdate() QApplication.restoreOverrideCursor() self.lastSVNCheck = time.time() if needsup: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("Your SVN repository is out of date.") msg.setInformativeText("Would you like to sync up now?") msg.setWindowTitle("SVN Repository out of date") msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msg.buttonClicked.connect(lambda i: self.onSyncOk(i)) self.ignoreSVN += 1 self.dialogUp = True msg.exec_() self.dialogUp = False def doSVNUpdate(self): QApplication.setOverrideCursor(Qt.WaitCursor) arttoolUpdated = svnUpdate(self.outputWindow) self.fillFbxList() QApplication.restoreOverrideCursor() if arttoolUpdated: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("File(s) in the Art Tool have changed.") msg.setInformativeText("You should exit the Art Tool now and start it again.") msg.setWindowTitle("Art Tool Changed") msg.setStandardButtons(QMessageBox.Ok) msg.buttonClicked.connect(lambda i: self.onSyncOk(i)) self.cancelledSVN = True msg.exec_() def onSyncOk(self, i): if i.text() == "OK": self.doSVNUpdate() else: # dont check anymore self.cancelledSVN = True def doAutobuild(self): QApplication.setOverrideCursor(Qt.WaitCursor) all_missing = dict() for f in self.fbxfiles: if doesFileNeedRebuilding(f): deps, missing = buildMask(f, self.outputWindow) if len(missing) > 0: all_missing[f] = missing for f in self.combofiles: if doesFileNeedRebuilding(f): deps, missing = buildCombo(f, self.outputWindow) if len(missing) > 0: all_missing[f] = missing for file, missing in all_missing.items(): for m in missing: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText(file +" depends on " + m + ", which cannot be found.") msg.setWindowTitle("Missing PNG File") msg.setStandardButtons(QMessageBox.Ok) self.ignoreSVN += 1 self.dialogUp = True msg.exec_() self.dialogUp = False QApplication.restoreOverrideCursor() self.fillComboList() self.fillFbxList() def doRebuildAll(self): msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("WARNING!! This will rebuild ALL THE MASKS!!") msg.setInformativeText("Are you abso-f*****g-lutely sure?") msg.setWindowTitle("Rebuild Everything") msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msg.buttonClicked.connect(lambda i: self.onRebuildAllOk(i)) self.ignoreSVN += 1 self.dialogUp = True msg.exec_() self.dialogUp = False def onRebuildAllOk(self, i): if i.text() == "OK": QApplication.setOverrideCursor(Qt.WaitCursor) all_missing = dict() for f in self.fbxfiles: deps, missing = buildMask(f, self.outputWindow) if len(missing) > 0: all_missing[f] = missing for f in self.combofiles: deps, missing = buildCombo(f, self.outputWindow) if len(missing) > 0: all_missing[f] = missing for file, missing in all_missing.items(): for m in missing: msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText(file + " depends on " + m + ", which cannot be found.") msg.setWindowTitle("Missing PNG File") msg.setStandardButtons(QMessageBox.Ok) self.ignoreSVN += 1 self.dialogUp = True msg.exec_() self.dialogUp = False QApplication.restoreOverrideCursor() self.fillComboList() self.fillFbxList() else: # dont check anymore self.cancelledSVN = True def doReleaseMasks(self): print("todo release masks") def uploadToS3(self): # what now msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText("Upload to S3, or just build index?") msg.setWindowTitle("Upload to S3") msg.setStandardButtons(QMessageBox.Ok|QMessageBox.Cancel) msg.buttonClicked.connect(lambda i: self.onUploadToS3Confirm(i)) self.ignoreSVN += 1 self.dialogUp = True msg.exec_() self.dialogUp = False def onUploadToS3Confirm(self, item): do_upload = item.text() == "OK" metalist = list() jsonlist = list() for fbxfile in self.fbxfiles: mdc, mt = checkMetaDataFile(fbxfile) if mdc == CHECKMETA_GOOD: metadata = loadMetadataFile(fbxfile) d = dict() for k in RELEASE_FIELDS: d[k] = metadata[k] if type(d[k]) is str: d[k] = d[k].replace("\n", "").replace("\r", "") metalist.append(d) jsonlist.append(jsonFromFbx(fbxfile)) for combofile in self.combofiles: mdc, mt = checkMetaDataFile(combofile) if mdc == CHECKMETA_GOOD: metadata = loadMetadataFile(combofile) d = dict() for k in RELEASE_FIELDS: d[k] = metadata[k] if type(d[k]) is str: d[k] = d[k].replace("\n", "").replace("\r", "") md = getCombinedComboMeta(metadata) d["tags"] = md["tags"] d["author"] = md["author"] metalist.append(d) jsonlist.append(combofile) for i in range(0,len(metalist)): metalist[i]["category"] = metalist[i]["category"].lower() metalist[i]["tags"] = metalist[i]["tags"].lower().replace(", ",",") metalist[i]["modtime"] = int(os.path.getmtime(jsonlist[i])) metalist[i]["author"] = metalist[i]["author"].replace(", ",",") if metalist[i]["tags"].endswith(" "): metalist[i]["tags"] = metalist[i]["tags"][:-1] metalist[i]["name"] = metalist[i]["name"].strip() metalist[i]["author"] = metalist[i]["author"].strip() if do_upload: print("Uploading",jsonlist[i]) uuid = metalist[i]["uuid"] print(" json...") s3_upload(jsonlist[i], uuid + ".json") print(" png...") s3_upload(jsonlist[i].replace(".json",".png"), uuid + ".png") print(" gif...") s3_upload(jsonlist[i].replace(".json",".gif"), uuid + ".gif") print(" mp4...") s3_upload(jsonlist[i].replace(".json",".mp4"), uuid + ".mp4") file, filter = QFileDialog.getSaveFileName(self, 'Save file', os.path.abspath("."), "Mask files (*.json)") if file is not None and len(file) > 0: writeMetaData(os.path.abspath(file), metalist) def onWriteMetadataExcel(self): pass """
class MainWindow(QMainWindow): """ 主窗口类 """ def __init__(self): super().__init__() self.item_cnt = 0 # 使用QListWidget来记录已有的图元,并用于选择图元。注:这是图元选择的简单实现方法,更好的实现是在画布中直接用鼠标选择图元 self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(200) # 使用QGraphicsView作为画布 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, 600, 600) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(600, 600) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget # 设置菜单栏 menubar = self.menuBar() file_menu = menubar.addMenu('文件') set_pen_menu = file_menu.addMenu('设置画笔') set_pen_color_act = set_pen_menu.addAction('设置画笔颜色') set_pen_width_act = set_pen_menu.addAction('设置画笔粗细') reset_canvas_act = file_menu.addAction('重置画布') exit_act = file_menu.addAction('退出') draw_menu = menubar.addMenu('绘制') line_menu = draw_menu.addMenu('线段') line_naive_act = line_menu.addAction('Naive') line_dda_act = line_menu.addAction('DDA') line_bresenham_act = line_menu.addAction('Bresenham') polygon_menu = draw_menu.addMenu('多边形') polygon_dda_act = polygon_menu.addAction('DDA') polygon_bresenham_act = polygon_menu.addAction('Bresenham') ellipse_act = draw_menu.addAction('椭圆') curve_menu = draw_menu.addMenu('曲线') curve_bezier_act = curve_menu.addAction('Bezier') curve_b_spline_act = curve_menu.addAction('B-spline') edit_menu = menubar.addMenu('编辑') translate_act = edit_menu.addAction('平移') rotate_act = edit_menu.addAction('旋转') scale_act = edit_menu.addAction('缩放') clip_menu = edit_menu.addMenu('裁剪') clip_cohen_sutherland_act = clip_menu.addAction('Cohen-Sutherland') clip_liang_barsky_act = clip_menu.addAction('Liang-Barsky') # 连接信号和槽函数 exit_act.triggered.connect(qApp.quit) line_naive_act.triggered.connect(self.line_naive_action) line_dda_act.triggered.connect(self.line_dda_action) line_bresenham_act.triggered.connect(self.line_bresenham_action) polygon_dda_act.triggered.connect(self.polygon_dda_action) polygon_bresenham_act.triggered.connect(self.polygon_bresenham_action) ellipse_act.triggered.connect(self.ellipse_action) curve_b_spline_act.triggered.connect(self.curve_b_spline_action) curve_bezier_act.triggered.connect(self.curve_bezier_action) translate_act.triggered.connect(self.translate_action) rotate_act.triggered.connect(self.rotate_action) scale_act.triggered.connect(self.scale_action) clip_cohen_sutherland_act.triggered.connect( self.clip_cohen_sutherland_action) clip_liang_barsky_act.triggered.connect(self.clip_liang_barsky_action) set_pen_color_act.triggered.connect(self.set_pen_color_action) set_pen_width_act.triggered.connect(self.set_pen_width_action) reset_canvas_act.triggered.connect(self.reset_canvas_action) self.list_widget.currentTextChanged.connect( self.canvas_widget.selection_changed) # 设置主窗口的布局 self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.statusBar().showMessage('空闲') self.resize(600, 600) self.setWindowTitle('CG Demo') def line_naive_action(self): self.canvas_widget.start_draw_line('Naive') self.statusBar().showMessage('Naive算法绘制线段,按住鼠标左键并移动以绘制直线,松开以结束绘制') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_dda_action(self): self.canvas_widget.start_draw_line('DDA') self.statusBar().showMessage('DDA算法绘制线段,按住鼠标左键并移动以绘制直线,松开以结束绘制') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_bresenham_action(self): self.canvas_widget.start_draw_line('Bresenham') self.statusBar().showMessage('Bresenham算法绘制线段,按住鼠标左键并移动以绘制直线,松开以结束绘制') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_dda_action(self): self.canvas_widget.start_draw_polygon('DDA') self.statusBar().showMessage('DDA算法绘制多边形,点击左键设置控制点,点击右键结束绘制') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_bresenham_action(self): self.canvas_widget.start_draw_polygon('Bresenham') self.statusBar().showMessage('Bresenham算法绘制多边形,点击左键设置控制点,点击右键结束绘制') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def ellipse_action(self): self.canvas_widget.start_draw_ellipse() self.statusBar().showMessage('中点圆生成算法绘制椭圆,按住鼠标左键并移动以绘制椭圆,松开以结束绘制') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_bezier_action(self): self.canvas_widget.start_draw_curve('Bezier') self.statusBar().showMessage('Bezier算法绘制曲线,点击左键设置控制点,点击右键结束绘制') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_b_spline_action(self): self.canvas_widget.start_draw_curve('B-spline') self.statusBar().showMessage('B-spline算法绘制曲线,点击左键设置控制点,点击右键结束绘制') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def translate_action(self): self.canvas_widget.start_translate() self.statusBar().showMessage('按住鼠标左键并移动以平移图元,松开以结束平移') def rotate_action(self): self.canvas_widget.start_rotate() self.statusBar().showMessage('按住鼠标左键并移动以旋转图元,松开以结束旋转') def scale_action(self): self.canvas_widget.start_scale() self.statusBar().showMessage('按住鼠标左键并移动以缩放图元,松开以结束缩放') def clip_cohen_sutherland_action(self): self.canvas_widget.start_clip('Cohen-Sutherland') self.statusBar().showMessage('按住鼠标左键并移动以产生裁剪框,松开以裁剪线段') def clip_liang_barsky_action(self): self.canvas_widget.start_clip('Liang-Barsky') self.statusBar().showMessage('按住鼠标左键并移动以产生裁剪框,松开以裁剪线段') def set_pen_color_action(self): c = QColorDialog.getColor() self.canvas_widget.set_pen_color(c) def set_pen_width_action(self): w, ok = QInputDialog.getInt(self, '调整粗细', '输入画笔粗细', value=self.canvas_widget.pen_width) if ok and w: self.canvas_widget.set_pen_width(w) def reset_canvas_action(self): self.item_cnt = 0 self.canvas_widget.reset_canvas() self.list_widget.currentTextChanged.disconnect( self.canvas_widget.selection_changed) self.list_widget.clear() self.list_widget.currentTextChanged.connect( self.canvas_widget.selection_changed) self.scene.clear()
class DifferenceDlg(QDialog): """ This is the dialog box that displays the differences between the export file and the reference file. exportList and referenceList contains the list of unmatched strings from the exported file and reference file respectively. fileName refers to the name of the file where the differences are found. """ def __init__(self, parent, exportList, referenceList, fileName): super(DifferenceDlg, self).__init__(parent) self.parent = parent self.fileName = fileName self.updatedLines = [] spacerHeight = 15 self.tabs = QTabWidget() self.tab1 = QWidget() self.tabs.addTab(self.tab1, "Export vs Reference") description = QLabel("The differences between the files: %s" % fileName) g1 = QGridLayout() reasonLabel = QLabel("<b>Reason <b>") g1.addWidget(reasonLabel, 0, 0, 1, 2) qhbL = QHBoxLayout() self.listWL = QListWidget() for items in exportList: # splitedString = items.split(':') # lineNo = splitedString[0] # eString = splitedString[1] # rString = 'Line no: ' + lineNo + ', ' + 'Error String: ' + eString self.listWL.addItem(items) qhbL.addWidget(self.listWL) self.listWR = QListWidget() for items in referenceList: # splitedString = items.split(',') # lineNo = splitedString[0] # eString = splitedString[1] # rString = 'Line no: ' + lineNo + ', ' + 'Error String: ' + eString self.listWR.addItem(items) qhbL.addWidget(self.listWR) promptLabel = QLabel("Please state reason for change.") inputLabel = QLabel("Input reason here:") self.reasonInput = QLineEdit() self.reasonInput.setPlaceholderText("") changeButton = QPushButton("Change") changeButton.clicked.connect(self.acceptChange) changeAllbutton = QPushButton("Change All") changeAllbutton.clicked.connect(self.acceptChangeAll) # ignoreButton = QPushButton("Ignore") # ignoreButton.clicked.connect(self.ignoreChange) g1.addWidget(promptLabel, 1, 0, 1, 1) g1.addWidget(inputLabel, 2, 0, 1, 1) g1.addWidget(self.reasonInput, 3, 0, 1, 3) g1.addWidget(changeButton, 4, 0, 1, 1) g1.addWidget(changeAllbutton, 4, 2, 1, 1) # g1.addWidget(ignoreButton, 4, 2, 1, 1) spaceHx = QSpacerItem(self.width(), spacerHeight) g1.addItem(spaceHx, 5, 0, 1, 2) self.FinishButton = QPushButton("Finish") self.FinishButton.clicked.connect(self.finishEdit) l = QVBoxLayout() l.addWidget(description) l.addWidget(reasonLabel) t1Layout = QVBoxLayout() t1Layout.addLayout(qhbL) t1Layout.addLayout(g1) self.tab1.setLayout(t1Layout) l.addWidget(self.tabs) l.addWidget(self.FinishButton) self.setLayout(l) self.setFixedSize(1000, 1000) self.listWR.setSelectionMode(1) self.listWL.setSelectionMode(1) self.listWR.clicked.connect(self.listWRClicked) self.listWL.clicked.connect(self.listWLClicked) self.exec_() def listWLClicked(self): self.listWR.clearSelection() def listWRClicked(self): self.listWL.clearSelection() def acceptChange(self): if len(self.listWR.selectedItems()) > 0: msg = QMessageBox() msg.setText("Please select from the left side list.") msg.exec_() if len(self.listWL.selectedItems()) > 0: for items in self.listWL.selectedItems(): # print(items.text()[:items.text().find(":")]) # print(items.text()[items.text().find(":")+1:]) lineNo = items.text()[:items.text().find(":")] string = items.text()[items.text().find(":") + 1:] self.updatedLines.append(items.text()) self.updateReferenceFile(lineNo, string) self.updateChangelog() msg2 = QMessageBox() msg2.setText("Changes updated in the reference file.") msg2.exec_() def acceptChangeAll(self): items = [] lineNoList = [] stringList = [] i = 0 while i < self.listWL.count(): items.append(self.listWL.item(i)) i += 1 for item in items: lineNo = item.text()[:item.text().find(":")] string = item.text()[item.text().find(":") + 1:] lineNoList.append(lineNo) stringList.append(string) self.updatedLines.append(item.text()) # self.updateReferenceFile(lineNo, string) self.updateAllChanges(lineNoList, stringList) self.updateChangelog() msg = QMessageBox() msg.setText("All changes updated in the reference file.") msg.exec_() def finishEdit(self): self.close() def updateReferenceFile(self, lineNo, string): """ Parameters ---------- lineNo : the line number of the unmatch string string : the correct string 1.Access the reference folder 2.read from the error file 3.Update the error lines 4.write to the error file Returns ------- """ if getattr(sys, "frozen", False): ROOT_DIR = os.path.dirname(sys.executable) elif __file__: ROOT_DIR = os.path.dirname(__file__) originalFilePath = os.path.join(ROOT_DIR, "Reference") fileToUpdate = os.path.join(originalFilePath, self.fileName) with open(fileToUpdate, "r") as file: lines = file.readlines() # print(lines) stringToAdd = string try: lines[int(lineNo) - 1] except IndexError: lines.append("") lines[int(lineNo) - 1] = stringToAdd with open(fileToUpdate, "w") as file: file.writelines(lines) print("finish running") def updateAllChanges(self, lineNoList, stringList): """ Parameters ---------- lineNo : the line number of the unmatch string string : the correct string 1.Access the reference folder 2.read from the error file 3.Update the error lines 4.write to the error file Returns ------- """ if getattr(sys, "frozen", False): ROOT_DIR = os.path.dirname(sys.executable) elif __file__: ROOT_DIR = os.path.dirname(__file__) originalFilePath = os.path.join(ROOT_DIR, "Reference") fileToUpdate = os.path.join(originalFilePath, self.fileName) with open(fileToUpdate, "r") as file: lines = file.readlines() i = 0 while i < len(lineNoList): try: lines[int(lineNoList[i]) - 1] except IndexError: lines.append("") lines[int(lineNoList[i]) - 1] = stringList[i] i += 1 with open(fileToUpdate, "w") as file: file.writelines(lines) print("finish running") def updateChangelog(self): linesToAppend = [] if getattr(sys, "frozen", False): ROOT_DIR = os.path.dirname(sys.executable) elif __file__: ROOT_DIR = os.path.dirname(__file__) changelogfile = os.path.join(ROOT_DIR, "changelogs.txt") reason = self.reasonInput.text() dateAndTime = datetime.now() dt_string = dateAndTime.strftime("%d/%m/%Y %H:%M:%S") Divider = "==============================================================\n" fileName = self.fileName updatedLines = self.updatedLines reasonHeader = "Reason for change:" differenceHeader = "Differences:" fileNameHeader = "Name of file:" linesToAppend.append(Divider) linesToAppend.append(dt_string + "\n" + "\n") linesToAppend.append(fileNameHeader + "\n") linesToAppend.append(fileName + "\n" + "\n") linesToAppend.append(differenceHeader + "\n") for lines in updatedLines: linesToAppend.append(lines) linesToAppend.append("\n" + reasonHeader + "\n") linesToAppend.append(reason + "\n") linesToAppend.append(Divider) with open(changelogfile, "a") as file: file.writelines(linesToAppend)
class TAFieldsTab(QWidget): def __init__(self, ctx): super(TAFieldsTab, self).__init__() self.ctx = ctx self._table_being_updated = False self.create_ui() def create_ui(self): self.fields_list = QListWidget() self.fields_list.itemSelectionChanged.connect( self.userchanged_field_list) self.mode_group = QGroupBox("Field Mode") self.mode_box = QComboBox() self.mode_box.addItem("Ignore", 'ignore') self.mode_box.addItem("Print Label", 'print') self.mode_box.addItem("Cluster", 'cluster') self.mode_box.addItem("Diversify", 'diversify') self.mode_box.currentIndexChanged.connect(self.userchanged_mode_box) echoLayout = QGridLayout() echoLayout.addWidget(QLabel("Mode:"), 0, 0) echoLayout.addWidget(self.mode_box, 0, 1) echoLayout.setHorizontalSpacing(50) echoLayout.setColumnStretch(0, 1) echoLayout.setColumnStretch(1, 1) self.mode_group.setLayout(echoLayout) self.terms_group = QGroupBox("Field Values") self.terms_layout = QStackedLayout() self.terms_layout.addWidget(self.create_empty_term_widget()) self.terms_layout.addWidget(self.create_table_term_widget()) self.terms_group.setLayout(self.terms_layout) layout = QGridLayout() layout.addWidget(self.fields_list, 0, 0, 2, 1) layout.addWidget(self.mode_group, 0, 1, 1, 1) layout.addWidget(self.terms_group, 1, 1) layout.setRowStretch(1, 1) self.setLayout(layout) def create_empty_term_widget(self): label = QLabel("Only applied for diversify and cluster categories.") label.setAlignment(Qt.AlignCenter) return label def create_table_term_widget(self): self.terms_table = QTableWidget(0, 2) self.terms_table.setHorizontalHeaderLabels( ['Terms Found', 'Terms Usage']) self.terms_table.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) self.terms_table.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self.terms_table.cellChanged.connect(self.userchanged_table) return self.terms_table def display_none(self): self.fields_list.clearSelection() self.status_mode_box(False) self.status_terms_group(False) def get_current_field_index(self): return self.fields_list.currentItem().data(Qt.UserRole) def init_field(self, j): if j not in self.ctx.app_data.fields: mode = 'ignore' terms = [[t, t] for t in self.ctx.app_data_manager.get_terms(j)] self.ctx.app_data.fields[j] = {'mode': mode, 'terms': terms} else: for t in self.ctx.app_data_manager.get_terms(j): if not any(a[0] == t for a in self.ctx.app_data.fields[j]['terms']): self.ctx.app_data.fields[j]['terms'].append([t, t]) for k, term_usage in enumerate( self.ctx.app_data.fields[j]['terms']): if term_usage[0] not in self.ctx.app_data_manager.get_terms(j): self.ctx.app_data.fields[j]['terms'].pop(k) def update_fields_list(self): self._field_list_being_updated = True self.fields_list.clear() for j, cat in enumerate(self.ctx.app_data.peopledata_keys): new_item = QListWidgetItem() new_item.setData(Qt.UserRole, j) new_item.setText(cat) self.fields_list.addItem(new_item) self._field_list_being_updated = False def update_mode_box(self, j): index = self.mode_box.findData(self.ctx.app_data.fields[j]['mode']) self.mode_box.setCurrentIndex(index) def update_terms_group(self, j): self._table_being_updated = True self.terms_table.setRowCount(len(self.ctx.app_data.fields[j]['terms'])) for k, term_usage in enumerate(self.ctx.app_data.fields[j]['terms']): item_col0 = QTableWidgetItem(term_usage[0]) item_col0.setFlags(Qt.ItemIsSelectable) self.terms_table.setItem(k, 0, item_col0) self.terms_table.setItem(k, 1, QTableWidgetItem(term_usage[1])) self._table_being_updated = False def status_mode_box(self, status): self.mode_group.setDisabled(not status) def status_terms_group(self, status): self.terms_group.setDisabled(not status) self.terms_layout.setCurrentIndex(1 if status else 0) def userchanged_field_list(self): if self._field_list_being_updated: return j = self.get_current_field_index() self.init_field(j) self.update_mode_box(j) self.update_terms_group(j) self.status_mode_box(True) mode = self.ctx.app_data.fields[j]['mode'] self.status_terms_group(True if mode in ['cluster', 'diversify'] else False) def userchanged_mode_box(self, index): j = self.get_current_field_index() mode = self.mode_box.currentData() self.ctx.app_data.fields[j]['mode'] = mode mode = self.ctx.app_data.fields[j]['mode'] self.status_terms_group(True if mode in ['cluster', 'diversify'] else False) self.ctx.window.tabs.fields_update() def userchanged_table(self, k, l): if self._table_being_updated: return j = self.get_current_field_index() self.ctx.app_data.fields[j]['terms'][k][1] = self.terms_table.item( k, l).text()
class MainWindow(QMainWindow): """ 主窗口类 """ def __init__(self): super().__init__() cglog.log("-----------------------------------") cglog.log("Start System") self.item_cnt = 0 # if not to set much larger Canvas will lead to # can not draw beyond original place # self.width = 2000 # self.height = 2000 self.width = 600 self.height = 600 # 使用QListWidget来记录已有的图元,并用于选择图元。注:这是图元选择的简单实现方法,更好的实现是在画布中直接用鼠标选择图元 self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(200) # 使用QGraphicsView作为画布 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, 600, 600) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(600, 600) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget # self.scene = QGraphicsScene(self) # self.scene.setSceneRect(0, 0, self.width, self.height) # self.canvas_widget = MyCanvas(self.scene, self) # self.canvas_widget.setFixedSize(self.width, self.height) # self.canvas_widget.main_window = self # self.canvas_widget.list_widget = self.list_widget self.canvas_widget.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) self.canvas_widget.setVerticalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) # 设置菜单栏 self.menubar = self.menuBar() file_menu = self.menubar.addMenu('文件') set_pen_act = file_menu.addAction('设置画笔') reset_canvas_act = file_menu.addAction('重置画布') clear_canvas_act = file_menu.addAction('清空画布') save_canvas_act = file_menu.addAction('保存画布') exit_act = file_menu.addAction('退出') draw_menu = self.menubar.addMenu('绘制') line_menu = draw_menu.addMenu('线段') line_naive_act = line_menu.addAction('Naive') line_dda_act = line_menu.addAction('DDA') line_bresenham_act = line_menu.addAction('Bresenham') polygon_menu = draw_menu.addMenu('多边形') polygon_dda_act = polygon_menu.addAction('DDA') polygon_bresenham_act = polygon_menu.addAction('Bresenham') ellipse_act = draw_menu.addAction('椭圆') curve_menu = draw_menu.addMenu('曲线') curve_bezier_act = curve_menu.addAction('Bezier') curve_b_spline_act = curve_menu.addAction('B-spline') edit_menu = self.menubar.addMenu('编辑') translate_act = edit_menu.addAction('平移') rotate_act = edit_menu.addAction('旋转') scale_act = edit_menu.addAction('缩放') clip_menu = edit_menu.addMenu('裁剪') clip_cohen_sutherland_act = clip_menu.addAction('Cohen-Sutherland') clip_liang_barsky_act = clip_menu.addAction('Liang-Barsky') #拓展功能的定义位置#.setShortcut("Ctrl+C") # I bind the shortcut here which can be triggered here self.Additional_function_menu = self.menubar.addMenu('附加功能') mouese_select_act = self.Additional_function_menu.addAction('鼠标选择图元') copy_act = self.Additional_function_menu.addAction('复制') copy_act.setShortcut("Ctrl+C") paste_act = self.Additional_function_menu.addAction("粘贴") paste_act.setShortcut("Ctrl+V") delete_act = self.Additional_function_menu.addAction("删除") delete_act.setShortcut("Ctrl+D") free_draw_act = self.Additional_function_menu.addAction("自由绘制") free_draw_act.setShortcut("Ctrl+F") # 关于菜单和窗口操作的信号绑定 exit_act.triggered.connect(self.quit_box) set_pen_act.triggered.connect(self.set_pen) mouese_select_act.triggered.connect(self.select_item_action) copy_act.triggered.connect(self.copy_action) paste_act.triggered.connect(self.paste_action) delete_act.triggered.connect(self.delete_action) free_draw_act.triggered.connect(self.free_draw_action) clear_canvas_act.triggered.connect(self.clear_canvas) save_canvas_act.triggered.connect(self.save_canvas) reset_canvas_act.triggered.connect(self.reset_canvas_action) self.list_widget.currentTextChanged.connect( self.canvas_widget.selection_changed) # 直线绘制算法的信号绑定 line_naive_act.triggered.connect(self.line_naive_action) line_dda_act.triggered.connect(self.line_DDA_action) line_bresenham_act.triggered.connect(self.line_bresenham_action) # 椭圆绘制算法信号绑定 ellipse_act.triggered.connect(self.ellipse_action) # 多边形绘制算法信号绑定 polygon_dda_act.triggered.connect(self.polygon_dda_action) polygon_bresenham_act.triggered.connect(self.polygon_bresenham_action) # 曲线绘制算法的信号绑定 curve_bezier_act.triggered.connect(self.curve_bezier_action) curve_b_spline_act.triggered.connect(self.curve_b_spline_action) # 关于编辑的算法信号绑定 translate_act.triggered.connect(self.translate_action) rotate_act.triggered.connect(self.rotate_action) scale_act.triggered.connect(self.scale_action) # 裁剪算法信号绑定 clip_cohen_sutherland_act.triggered.connect( self.clip_cohen_sutherland_action) clip_liang_barsky_act.triggered.connect(self.clip_liang_barsky_action) # 设置主窗口的布局 self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.statusBar().showMessage('空闲') self.resize(self.width, self.height) self.setWindowTitle('CG Demo') # self.width=600 # self.height=600 # self.scene = QGraphicsScene(self) # self.scene.setSceneRect(0, 0, self.width, self.height) # self.canvas_widget.resize(self.width, self.height) # self.canvas_widget.setFixedSize(self.width, self.height) # self.statusBar().showMessage('空闲') # self.setMaximumHeight(self.height) # self.setMaximumWidth(self.width) # self.resize(self.width, self.height) def get_id(self): _id = str(self.item_cnt) self.item_cnt += 1 return _id def copy_action(self): self.canvas_widget.copy_item() self.statusBar().showMessage('复制操作') def paste_action(self): self.canvas_widget.paste_item() self.statusBar().showMessage('粘贴操作') def delete_action(self): self.canvas_widget.delete_item() self.statusBar().showMessage('删除操作') def reset_canvas_action(self): print("Reset canvas") self.item_cnt = 0 self.statusBar().showMessage('重置画布') dialog = QDialog() dialog.setWindowTitle('重置画布') formlayout = QFormLayout(dialog) widthEdit = QLineEdit() heightEdit = QLineEdit() formlayout.addRow("width", widthEdit) formlayout.addRow("height", heightEdit) box3 = QDialogButtonBox(QDialogButtonBox.Ok) box3.accepted.connect(dialog.accept) formlayout.addRow(box3) if dialog.exec(): width = widthEdit.text() height = heightEdit.text() if len(width) == 0 or len(height) == 0: QMessageBox.about(self, "Warning", "input number empty! ") return if width.isdigit() and height.isdigit(): if int(width) < 1000 and int(width) > 100 and int( height) < 1000 and int(height) > 100: self.width = int(width) self.height = int(height) else: QMessageBox.about( self, "Warning", "input number is out of range (100-1000 Only)! ") return else: QMessageBox.about(self, "Warning", "input number is not integer! ") return # print(self.width,self.height) # self.w = self.width # self.h = self.height self.canvas_widget.clear_canvas() self.list_widget.clearSelection() self.canvas_widget.clear_selection() self.list_widget.clear() #self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, self.width, self.height) self.canvas_widget.resize(self.width, self.height) self.canvas_widget.setFixedSize(self.width, self.height) self.statusBar().showMessage('空闲') self.setMaximumHeight(self.height) self.setMaximumWidth(self.width) self.resize(self.width, self.height) def line_naive_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_line('Naive', self.get_id()) self.statusBar().showMessage('Naive算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_DDA_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_line('DDA', self.get_id()) self.statusBar().showMessage('DDA算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_bresenham_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_line('Bresenham', self.get_id()) self.statusBar().showMessage('Bresenham算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def ellipse_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_ellipse(self.get_id()) self.statusBar().showMessage('绘制椭圆') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_dda_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_polygon('DDA', self.get_id()) self.statusBar().showMessage('DDA算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_bresenham_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_polygon('Bresenham', self.get_id()) self.statusBar().showMessage('Bresenham算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_bezier_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_curve('Bezier', self.get_id()) self.statusBar().showMessage('Bezier算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_b_spline_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.canvas_widget.start_draw_curve('B-spline', self.get_id()) self.statusBar().showMessage('b_spline算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def select_item_action(self): self.statusBar().showMessage("使用鼠标选择图元") self.canvas_widget.start_select() def clear_canvas(self): self.statusBar().showMessage("清空画布") self.list_widget.clear() self.canvas_widget.clear_canvas() def save_canvas(self): self.statusBar().showMessage("保存画布") self.list_widget.clearSelection() self.canvas_widget.clear_selection() self.statusBar().showMessage('保存画布') dialog = QFileDialog() filename = dialog.getSaveFileName( filter="Image Files(*.jpg *.png *.bmp)") if filename[0]: res = self.canvas_widget.grab( self.canvas_widget.sceneRect().toRect()) res.save(filename[0]) def set_pen(self): # use this fuction to set pen's color self.statusBar().showMessage("设置画笔") col = QColorDialog.getColor() if col.isValid(): self.canvas_widget.set_pen_color(col) self.list_widget.clearSelection() self.canvas_widget.clear_selection() def translate_action(self): self.canvas_widget.start_translate() self.statusBar().showMessage('平移') def rotate_action(self): self.canvas_widget.start_rotate() self.statusBar().showMessage('旋转') def scale_action(self): self.canvas_widget.start_scale() self.statusBar().showMessage('缩放') def clip_cohen_sutherland_action(self): self.canvas_widget.start_clip_cohen_sutherland() self.statusBar().showMessage('裁剪cohen_sutherland') def clip_liang_barsky_action(self): self.canvas_widget.start_clip_liang_barsky() self.statusBar().showMessage('裁剪liang_barsky') def free_draw_action(self): if (self.item_cnt > 0): self.item_cnt -= 1 self.statusBar().showMessage('自由绘制') self.canvas_widget.draw_free(self.get_id()) def quit_box(self): reply = QMessageBox.question(self, 'CG System', "是否要保存画布", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.save_canvas() qApp.quit() # else: # event.ignore() def closeEvent(self, event): reply = QMessageBox.question(self, 'CG System', "是否要保存画布", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.save_canvas() qApp.quit()
class TrainTab(QWidget): def __init__(self): super().__init__() self.available_models = [] self.model = None self.model_dir = None self.weights_name = None self.highlighted_data = None self.batch_size = 50 self.epochs = 100 self.initUI() def initUI(self): self.train_button = QPushButton("Train") self.train_button.toggle() self.train_button.setCheckable(True) self.train_button.setEnabled(False) self.train_button.clicked.connect(self.toggle_train_button) self.weights_name_field = QLineEdit() self.weights_name_field.textChanged.connect(self.weights_name_changed) self.weights_name_field.setEnabled(False) self.data_list = QListWidget() self.data_list.setEnabled(False) self.data_list.itemClicked.connect(self.highlight_available_data) self.added_data_list = QListWidget() self.added_data_list.setEnabled(False) self.added_data_list.itemClicked.connect(self.highlight_added_data_list) self.move_data_button = QPushButton("<< >>") self.move_data_button.clicked.connect(self.move_data) self.move_data_button.setEnabled(False) batch_size_widget = QLineEdit(str(self.batch_size)) batch_size_widget.setValidator(QIntValidator()) batch_size_widget.textChanged.connect(lambda new_val: self.line_edit_value_changed(new_val, "batch_size")) epochs_widget = QLineEdit(str(self.epochs)) epochs_widget.setValidator(QIntValidator()) epochs_widget.textChanged.connect(lambda new_val: self.line_edit_value_changed(new_val, "epochs")) model_list = self.init_model_list() menu_widget = QWidget() menu_layout = QGridLayout() menu_layout.setColumnStretch(1, 1) menu_layout.setColumnStretch(2, 2) menu_layout.setColumnStretch(3, 1) menu_layout.setColumnStretch(4, 2) menu_layout.setColumnStretch(5, 2) menu_layout.addWidget(QWidget()) menu_layout.addWidget(QLabel("Select model: "), 1, 1) menu_layout.addWidget(model_list, 1, 2) menu_layout.addWidget(QLabel("Available data:"), 2, 2, Qt.AlignCenter) menu_layout.addWidget(QLabel("Selected data:"), 2, 4, Qt.AlignCenter) menu_layout.addWidget(QLabel("Select data: "), 3, 1) menu_layout.addWidget(self.data_list, 3, 2) menu_layout.addWidget(self.move_data_button, 3, 3) menu_layout.addWidget(self.added_data_list, 3, 4) menu_layout.addWidget(QLabel("Batch size: "), 4, 1) menu_layout.addWidget(batch_size_widget, 4, 2) menu_layout.addWidget(QLabel("Epochs: "), 5, 1) menu_layout.addWidget(epochs_widget, 5, 2) menu_layout.addWidget(QLabel("Name for weights: "), 6,1) menu_layout.addWidget(self.weights_name_field, 6,2) menu_layout.addWidget(self.train_button, 7, 1) menu_layout.addWidget(QWidget()) menu_widget.setLayout(menu_layout) main_layout = QVBoxLayout() main_layout.addWidget(menu_widget) self.setLayout(main_layout) def init_model_list(self): model_list = QComboBox() model_list.activated.connect(self.select_model) models_path = Path(os.getcwd()) / "models" for model_folder in models_path.iterdir(): if not model_folder.is_dir() or model_folder.name == "__pycache__": continue model_list.addItem(model_folder.name) self.available_models.append(model_folder) if self.available_models: self.select_model(0) return model_list def toggle_train_button(self): if self.train_button.isChecked(): self.train_button.setStyleSheet("background-color: orange") self.train_button.setEnabled(False) QApplication.processEvents() self.train() def select_model(self, idx): self.model_dir = self.available_models[idx] module_path = str((self.model_dir / "model.py").absolute()) spec = importlib.util.spec_from_file_location("model", module_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) self.model = module.KodoModel() self.weights_name_field.setEnabled(True) self.refresh_data_list() def weights_name_changed(self, new_name): if not self.weights_name: self.train_button.setEnabled(True) self.weights_name = new_name def refresh_data_list(self): self.move_data_button.setEnabled(True) self.added_data_list.setEnabled(True) self.data_list.setEnabled(True) data_path = self.model_dir / "data" self.data_list.clear() for data in data_path.iterdir(): if not data.is_dir() or data.name == "__pycache__": continue self.data_list.addItem(data.name) def highlight_available_data(self, item): self.highlighted_data = item.text() self.added_data_list.clearSelection() def highlight_added_data_list(self, item): self.highlighted_data = item.text() self.data_list.clearSelection() def move_data(self): if self.is_in_qlist(self.highlighted_data, self.data_list): self.remove_from_qlist(self.highlighted_data, self.data_list) self.added_data_list.addItem(self.highlighted_data) if self.data_list.currentItem(): self.highlighted_data = self.data_list.currentItem().text() else: self.remove_from_qlist(self.highlighted_data, self.added_data_list) self.data_list.addItem(self.highlighted_data) if self.added_data_list.currentItem(): self.highlighted_data = self.added_data_list.currentItem().text() def is_in_qlist(self, text, qlist): for idx in range(qlist.count()): item = qlist.item(idx) if item.text() == text: return True return False def remove_from_qlist(self, text, qlist): for idx in range(qlist.count()): item = qlist.item(idx) if item.text() == text: qlist.takeItem(idx) break def get_added_data_names(self): names = [] for idx in range(self.added_data_list.count()): name = self.added_data_list.item(idx).text() names.append(name) return names def stack_data(self): info = None data_lists = [] for name in self.get_added_data_names(): data_path = self.model_dir / "data" / name if not data_lists: for idx, data in enumerate(self.model.load_processed_data(data_path)): data_lists.append([data]) else: for idx, data in enumerate(self.model.load_processed_data(data_path)): data_lists[idx].append(data) with open(data_path / "info.json") as fp: info = json.load(fp) stacks = [] for data_list in data_lists: stacks.append(np.concatenate(data_list, axis=0)) stacks.append(info) return stacks def train(self): self.model.load_data_into_variables(self.stack_data()) self.model.create_model() self.model.train(batch_size=self.batch_size, epochs=self.epochs, weights_name=self.weights_name) self.train_button.setChecked(False) self.train_button.setEnabled(True) self.train_button.setStyleSheet("") def line_edit_value_changed(self, new_val, variable_name): try: # Segmentation fault if screen_capture_size set to 1 if int(new_val) > 1: setattr(self, variable_name, int(new_val)) except ValueError: pass
class VarTabWidgetDemo(QWidget): def __init__(self, data_list): super(VarTabWidgetDemo, self).__init__() self.initUI(data_list) def initUI(self, data_list): self.resize(800, 800) self.setWindowTitle('变量设置') self.signal = MySignal() icon = QIcon() icon.addPixmap(QPixmap('./image/设置.png')) self.setWindowIcon(icon) self.label1 = QLabel('变量') self.label2 = QLabel('自变量') self.label3 = QLabel('因变量') self.list_widget1 = QListWidget() self.list_widget2 = QListWidget() self.list_widget3 = QListWidget() self.list_widget1.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_widget2.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_widget3.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_widget1.addItems([str(item) for item in data_list]) self.button12 = QPushButton('-->') self.button13 = QPushButton('-->') vlayout1 = QVBoxLayout() vlayout1.addWidget(self.button12) hlayout1 = QHBoxLayout() hlayout1.addItem(vlayout1) vlayout = QVBoxLayout() top_hlayout2 = QHBoxLayout() self.checkBox2 = QCheckBox('全选') top_hlayout2.addWidget(self.label2) top_hlayout2.addWidget(self.checkBox2) top_hlayout2.addStretch() vlayout.addLayout(top_hlayout2) vlayout.addWidget(self.list_widget2) vlayout.setSpacing(10) hlayout1.addItem(vlayout) vlayout2 = QVBoxLayout() vlayout2.addWidget(self.button13) hlayout2 = QHBoxLayout() hlayout2.addItem(vlayout2) vlayout = QVBoxLayout() top_hlayout3 = QHBoxLayout() self.checkBox3 = QCheckBox('全选') top_hlayout3.addWidget(self.label3) top_hlayout3.addWidget(self.checkBox3) top_hlayout3.addStretch() vlayout.addLayout(top_hlayout3) vlayout.addWidget(self.list_widget3) vlayout.setSpacing(10) hlayout2.addItem(vlayout) gridlayout = QGridLayout() hlayout = QHBoxLayout() hlayout.setSpacing(20) vlayout = QVBoxLayout() vlayout.addItem(hlayout) top_hlayout1 = QHBoxLayout() self.checkBox1 = QCheckBox('全选') top_hlayout1.addWidget(self.label1) top_hlayout1.addWidget(self.checkBox1) top_hlayout1.addStretch() vlayout.addLayout(top_hlayout1) vlayout.addWidget(self.list_widget1) vlayout.setSpacing(10) gridlayout.addItem(vlayout, 1, 0, 2, 1) hlayout1.setSpacing(10) hlayout2.setSpacing(10) gridlayout.addItem(hlayout1, 1, 1, 1, 1) gridlayout.addItem(hlayout2, 2, 1, 1, 1) self.setLayout(gridlayout) self.button12.clicked.connect(self.onClickButton12) self.button13.clicked.connect(self.onClickButton13) self.list_widget1.setObjectName('list_widget1') self.list_widget2.setObjectName('list_widget2') self.list_widget3.setObjectName('list_widget3') self.list_widget1.setToolTip('按住Ctrl键可用鼠标多选哦!') self.list_widget2.setToolTip('按住Ctrl键可用鼠标多选哦!') self.list_widget3.setToolTip('按住Ctrl键可用鼠标多选哦!') self.list_widget1.itemClicked.connect(self.focusList) self.list_widget2.itemClicked.connect(self.focusList) self.list_widget3.itemClicked.connect(self.focusList) self.list_widget1.itemSelectionChanged.connect(self.focusList) self.list_widget2.itemSelectionChanged.connect(self.focusList) self.list_widget3.itemSelectionChanged.connect(self.focusList) self.checkBox1.stateChanged.connect(self.checkBoxStatus) self.checkBox2.stateChanged.connect(self.checkBoxStatus) self.checkBox3.stateChanged.connect(self.checkBoxStatus) #鼠标在特定list_widget操作时改变按钮显示的操作方向 def focusList(self): sender = self.sender().objectName() if sender == 'list_widget1': if self.button12.text() != '-->': self.button12.setText('-->') if self.button13.text() != '-->': self.button13.setText('-->') elif sender == 'list_widget2': if self.button12.text() != '<--': self.button12.setText('<--') else: if self.button13.text() != '<--': self.button13.setText('<--') def onClickButton12(self): if self.button12.text() == '-->': sender = self.list_widget1 reciever = self.list_widget2 else: sender = self.list_widget2 reciever = self.list_widget1 if sender is not None and reciever is not None: self.sendData(sender, reciever) def onClickButton13(self): sender, reciever = None, None if self.button13.text() == '-->': sender = self.list_widget1 reciever = self.list_widget3 else: sender = self.list_widget3 reciever = self.list_widget1 if sender is not None and reciever is not None: self.sendData(sender, reciever) def sendData(self, sender, reciever): try: item_list = sender.selectedItems() for item in item_list: reciever.addItem(item.text()) sender.takeItem(sender.row(item)) self.initCheckBox() except Exception as e: print(e) def checkBoxStatus(self): sender = self.sender() if (sender == self.checkBox1): if self.checkBox1.isChecked(): self.list_widget1.selectAll() else: self.list_widget1.clearSelection() elif (sender == self.checkBox2): if self.checkBox2.isChecked(): self.list_widget2.selectAll() else: self.list_widget2.clearSelection() else: if self.checkBox1.isChecked(): self.list_widget3.selectAll() else: self.list_widget3.clearSelection() def initCheckBox(self): self.checkBox1.setChecked(False) self.checkBox2.setChecked(False) self.checkBox3.setChecked(False) def getVarDict(self): count1 = self.list_widget2.count() count2 = self.list_widget3.count() independ_var = [ self.list_widget2.item(i).text() for i in range(count1) ] depend_var = [self.list_widget3.item(i).text() for i in range(count2)] var_dict = {'independ_var': independ_var, 'depend_var': depend_var} return var_dict
class ConfigureStorageDialog(QDialog): WIDTH_INCREMENT = 10 HEIGHT_INCREMENT = 100 def __init__(self, storage: _st.StorageTank, parent: _ed.Editor): super().__init__(parent) self.parent = parent self.storage = storage self.n = 0 self.m = 0 spacerHeight = 15 self.tabs = QTabWidget() self.tab1 = QWidget() self.tab2 = QWidget() self.tabs.addTab(self.tab1, "Heat exchangers") self.tabs.addTab(self.tab2, "Direct ports") h0 = QHBoxLayout() description = QLabel("Please configure the storage tank:") exportButton = QPushButton("Export ddck") exportButton.clicked.connect(self.storage.exportDck) h0.addWidget(description) h0.addWidget(exportButton) tankNameLabel = QLabel() tankNameLabel.setText("<b>Tank name: </b>") self.le = QLineEdit(self.storage.label.toPlainText()) gl = QGridLayout() hxsLabel = QLabel("<b>Heat Exchangers </b>") gl.addWidget(hxsLabel, 0, 0, 1, 2) qhbL = QHBoxLayout() self.leftHeatExchangersItemListWidget = QListWidget() qhbL.addWidget(self.leftHeatExchangersItemListWidget) self.rightHeatExchangersItemListWidget = QListWidget() qhbL.addWidget(self.rightHeatExchangersItemListWidget) offsetLabel = QLabel("Height offsets in percent") offsetLeILabel = QLabel("Input (upper port): ") offsetLeOLabel = QLabel("Output (lower port):") self.offsetLeI = QLineEdit("0") self.offsetLeO = QLineEdit("0") self.lButton = QRadioButton("Left side") self.rButton = QRadioButton("Right side") gl.addWidget(offsetLabel, 3, 0, 1, 1) gl.addWidget(offsetLeILabel, 4, 0, 1, 1) gl.addWidget(self.offsetLeI, 5, 0, 1, 3) gl.addWidget(offsetLeOLabel, 6, 0, 1, 1) gl.addWidget(self.offsetLeO, 7, 0, 1, 3) self.hxNameLe = QLineEdit() self.hxNameLe.setPlaceholderText("Enter a name...") gl.addWidget(self.hxNameLe, 8, 0, 1, 3) gl.addWidget(self.lButton, 9, 0, 1, 1) gl.addWidget(self.rButton, 9, 2, 1, 1) addButton = QPushButton("Add...") addButton.clicked.connect(self.addHx) gl.addWidget(addButton, 10, 0, 1, 1) removeButton = QPushButton("Remove...") removeButton.clicked.connect(self.removeHxL) removeButton.clicked.connect(self.removeHxR) gl.addWidget(removeButton, 10, 1, 1, 1) modifyButton = QPushButton("Modify") modifyButton.clicked.connect(self.modifyHx) gl.addWidget(modifyButton, 10, 2, 1, 1) spaceHx = QSpacerItem(self.width(), spacerHeight) gl.addItem(spaceHx, 11, 0, 1, 2) manPortLay = QVBoxLayout() qhbL2 = QHBoxLayout() self._leftDirectPortPairsItemListWidget = QListWidget() qhbL2.addWidget(self._leftDirectPortPairsItemListWidget) self._rightDirectPortPairsItemListWidget = QListWidget() qhbL2.addWidget(self._rightDirectPortPairsItemListWidget) manPortLay.addLayout(qhbL2) manPortLabel = QLabel("<b>Set port manually</b>") manPortLabel2 = QLabel("Enter height in percent: ") portlabelUpper = QLabel("Inlet") self.manPortLeI = QLineEdit("0") portlabelLower = QLabel("Outlet") self.manPortLeO = QLineEdit("0") qhbl3 = QHBoxLayout() self.manlButton = QRadioButton("Left side") self.manrButton = QRadioButton("Right side") qhbl3.addWidget(self.manlButton) qhbl3.addWidget(self.manrButton) self.manAddButton = QPushButton("Add (manual) ports") self.manAddButton.clicked.connect(self.addPortPair) self.manRemovebutton = QPushButton("Remove ports") self.manRemovebutton.clicked.connect(self.removePortPairLeft) self.manRemovebutton.clicked.connect(self.removePortPairRight) self.modifyPortButton = QPushButton("Modify") self.modifyPortButton.clicked.connect(self.modifyPort) addRemoveButtons = QHBoxLayout() addRemoveButtons.addWidget(self.manAddButton) addRemoveButtons.addWidget(self.manRemovebutton) addRemoveButtons.addWidget(self.modifyPortButton) manPortLay.addWidget(manPortLabel) manPortLay.addWidget(manPortLabel2) manPortLay.addWidget(portlabelUpper) manPortLay.addWidget(self.manPortLeI) manPortLay.addWidget(portlabelLower) manPortLay.addWidget(self.manPortLeO) manPortLay.addLayout(qhbl3) manPortLay.addLayout(addRemoveButtons) increaseSizeButton = QPushButton("Increase size") decreaseSizeButton = QPushButton("Decrease size") self.okButton = QPushButton("OK") self.cancelButton = QPushButton("Cancel") increaseSizeButton.clicked.connect(self.incrSize) decreaseSizeButton.clicked.connect(self.decrSize) self.okButton.clicked.connect(self.acceptedEdit) self.cancelButton.clicked.connect(self.cancel) L = QVBoxLayout() L.addLayout(h0) L.addWidget(tankNameLabel) L.addWidget(self.le) t1Layout = QVBoxLayout() t1Layout.addLayout(qhbL) t1Layout.addLayout(gl) self.tab1.setLayout(t1Layout) self.tab2.setLayout(manPortLay) L.addWidget(self.tabs) L.addWidget(increaseSizeButton) L.addWidget(decreaseSizeButton) L.addWidget(self.okButton) L.addWidget(self.cancelButton) self.setLayout(L) self._loadHeatExchangers() self._loadDirectPortPairs() # This is to ensure that only one list element is selected self.rightHeatExchangersItemListWidget.setSelectionMode(1) self.leftHeatExchangersItemListWidget.setSelectionMode(1) self.rightHeatExchangersItemListWidget.clicked.connect( self.listWRClicked) self.leftHeatExchangersItemListWidget.clicked.connect( self.listWLClicked) self._rightDirectPortPairsItemListWidget.setSelectionMode(1) self._leftDirectPortPairsItemListWidget.setSelectionMode(1) self._rightDirectPortPairsItemListWidget.clicked.connect( self.listWR2Clicked) self._leftDirectPortPairsItemListWidget.clicked.connect( self.listWL2Clicked) self.show() def _loadHeatExchangers(self): self.leftHeatExchangersItemListWidget.clear() self.rightHeatExchangersItemListWidget.clear() for heatExchanger in self.storage.heatExchangers: itemText = self._getHeatExchangerListItemText(heatExchanger) item = QListWidgetItem(itemText) item.setData(_qtc.Qt.UserRole, heatExchanger) if heatExchanger.sSide == 0: self.leftHeatExchangersItemListWidget.addItem(item) else: self.rightHeatExchangersItemListWidget.addItem(item) @staticmethod def _getHeatExchangerListItemText(h): return (h.displayName + ", y_offset = " + "%d" % int(h.relativeInputHeight * 100) + "%" + " to " + "%d" % int(h.relativeOutputHeight * 100) + "%") def _loadDirectPortPairs(self): self._leftDirectPortPairsItemListWidget.clear() self._rightDirectPortPairsItemListWidget.clear() directPortPair: _dpp.DirectPortPair for directPortPair in self.storage.directPortPairs: itemText = self._getDirectPortPairListItemText(directPortPair) item = QListWidgetItem(itemText) item.setData(_qtc.Qt.UserRole, directPortPair) if directPortPair.side.isLeft: self._leftDirectPortPairsItemListWidget.addItem(item) else: self._rightDirectPortPairsItemListWidget.addItem(item) @staticmethod def _getDirectPortPairListItemText(directPortPair: _dpp.DirectPortPair): return ("Port pair from " + "%d%%" % int(directPortPair.relativeInputHeight * 100) + " to " + "%d%%" % int(directPortPair.relativeOutputHeight * 100)) def listWLClicked(self): self.rightHeatExchangersItemListWidget.clearSelection() def listWRClicked(self): self.leftHeatExchangersItemListWidget.clearSelection() def listWL2Clicked(self): self._rightDirectPortPairsItemListWidget.clearSelection() def listWR2Clicked(self): self._leftDirectPortPairsItemListWidget.clearSelection() def addHx(self): """ Checks whether the inputs are in the correct range (0,100) and in order, and calls the function for creating a HeatExchanger on the respective side. Returns ------- """ try: _inputPercentageHeight = float(self.offsetLeI.text()) except: self.parent.logger.warning('HX input height is not a number.') return try: _outputPercentageHeight = float(self.offsetLeO.text()) except: self.parent.logger.warning('HX output height is not a number.') return if (self.minOffsetDistance() and _inputPercentageHeight > _outputPercentageHeight and self.offsetsInRange()): if self.rButton.isChecked(): self.parent.logger.debug('Adding HX on righthand side.') self._addHxR() elif self.lButton.isChecked(): self.parent.logger.debug('Adding HX on lefthand side.') self._addHxL() else: self.parent.logger.warning( 'No side selected for heat exchanger.') return else: msgb = QMessageBox() msgb.setText( "At least 20% of difference and larger top port than bottom port needed and valid range (0, 100)" ) msgb.exec_() def minOffsetDistance(self): return abs( float(self.offsetLeI.text()) - float(self.offsetLeO.text())) >= 5 def offsetsInRange(self): return (0 <= float(self.offsetLeI.text()) <= 100) and (0 <= float( self.offsetLeO.text()) <= 100) def _addHxL(self): self._addHeatExchanger(_sd.Side.LEFT) def _addHxR(self): self._addHeatExchanger(_sd.Side.RIGHT) def _addHeatExchanger(self, side: _sd.Side): name = self.hxNameLe.text() if not name: messageBox = QMessageBox() messageBox.setText( "Please specify the name of the heat exchanger that you want to add." ) messageBox.exec_() return relativeInputHeight = float(self.offsetLeI.text()) / 100 relativeOutputHeight = float(self.offsetLeO.text()) / 100 trnsysId = self.parent.idGen.getTrnsysID() self.storage.addHeatExchanger(name, trnsysId, side, relativeInputHeight, relativeOutputHeight) self._loadHeatExchangers() def addPortPair(self): try: _inputPortPercentageHeight = float(self.manPortLeI.text()) except: self.parent.logger.warning('Input port height is not a number.') return try: _outputPortPercentageHeight = float(self.manPortLeO.text()) except: self.parent.logger.warning('Output port height is not a number.') return if max(_inputPortPercentageHeight, _outputPortPercentageHeight) >= 100 or min( _inputPortPercentageHeight, _outputPortPercentageHeight) <= 0: messageBox = QMessageBox() messageBox.setText( 'Ports need to be on the tank, please make sure the port heights are within (0 %, 100 %).' ) messageBox.exec_() return trnsysId = self.parent.idGen.getTrnsysID() if self.manlButton.isChecked(): _pairSide = _sd.Side.LEFT elif self.manrButton.isChecked(): _pairSide = _sd.Side.RIGHT else: self.parent.logger.warning('No side selected for port pair.') return self.storage.addDirectPortPair( trnsysId, _pairSide, _inputPortPercentageHeight / 100, _outputPortPercentageHeight / 100, self.storage.h, ) self._loadDirectPortPairs() def removePortPairLeft(self): self._removeSelectedPortPairs(self._leftDirectPortPairsItemListWidget) def removePortPairRight(self): self._removeSelectedPortPairs(self._rightDirectPortPairsItemListWidget) def _removeSelectedPortPairs(self, directPortPairsListWidget): for selectedItem in directPortPairsListWidget.selectedItems(): selectedDirectPortPair = selectedItem.data(_qtc.Qt.UserRole) self.storage.directPortPairs.remove(selectedDirectPortPair) row = directPortPairsListWidget.row(selectedItem) directPortPairsListWidget.takeItem(row) while len(selectedDirectPortPair.fromPort.connectionList) > 0: selectedDirectPortPair.fromPort.connectionList[0].deleteConn() while len(selectedDirectPortPair.toPort.connectionList) > 0: selectedDirectPortPair.toPort.connectionList[0].deleteConn() self.storage.inputs.remove(selectedDirectPortPair.fromPort) self.storage.outputs.remove(selectedDirectPortPair.toPort) self.storage.parent.scene().removeItem( selectedDirectPortPair.fromPort) self.storage.parent.scene().removeItem( selectedDirectPortPair.toPort) def removeHxL(self): self._removeSelectedHeatExchangers( self.leftHeatExchangersItemListWidget) def removeHxR(self): self._removeSelectedHeatExchangers( self.rightHeatExchangersItemListWidget) def _removeSelectedHeatExchangers(self, heatExchangersItemListWidget): for selectedItem in heatExchangersItemListWidget.selectedItems(): heatExchanger = selectedItem.data(_qtc.Qt.UserRole) self.storage.heatExchangers.remove(heatExchanger) row = heatExchangersItemListWidget.row(selectedItem) heatExchangersItemListWidget.takeItem(row) while len(heatExchanger.port1.connectionList) > 0: heatExchanger.port1.connectionList[0].deleteConn() while len(heatExchanger.port2.connectionList) > 0: heatExchanger.port2.connectionList[0].deleteConn() self.storage.inputs.remove(heatExchanger.port1) self.storage.outputs.remove(heatExchanger.port2) self.storage.parent.scene().removeItem(heatExchanger.port1) self.storage.parent.scene().removeItem(heatExchanger.port2) self.storage.parent.scene().removeItem(heatExchanger) def modifyHx(self): """ Modify Hx. """ result = self._getFirstSelectedItemAndHeatExchanger() if not result: return selectedItem, heatExchanger = result modifyDialog = _mhd.ModifyRelativeHeightsDialog( heatExchanger.relativeInputHeight, heatExchanger.relativeOutputHeight) newHeights = modifyDialog.newRelativeHeights if not newHeights: return newInputHeight = newHeights.input if newHeights.input != "empty" else heatExchanger.relativeInputHeight newOutputHeight = newHeights.output if newHeights.output != "empty" else heatExchanger.relativeOutputHeight heatExchanger.setRelativeHeights(newInputHeight, newOutputHeight) listText = self._getHeatExchangerListItemText(heatExchanger) selectedItem.setText(listText) def _getFirstSelectedItemAndHeatExchanger(self): leftSelectedItems = self.leftHeatExchangersItemListWidget.selectedItems( ) rightSelectedItems = self.rightHeatExchangersItemListWidget.selectedItems( ) if leftSelectedItems: side = 0 selectedItem = leftSelectedItems[0] elif rightSelectedItems: side = 2 selectedItem = rightSelectedItems[0] else: return None name = selectedItem.text().split(",")[0] for heatExchanger in self.storage.heatExchangers: if heatExchanger.displayName == name and heatExchanger.sSide == side: return selectedItem, heatExchanger raise AssertionError(f"No heat exchanger with name {name} found.") def modifyPort(self): """ Modify existing ports. """ selectedItem = self._getFirstSelectedDirectPortPairListWidgetItem() if not selectedItem: return directPortPair: _dpp.DirectPortPair = selectedItem.data( _qtc.Qt.UserRole) dialogResult = _mhd.ModifyRelativeHeightsDialog( directPortPair.relativeInputHeight, directPortPair.relativeOutputHeight) newHeights = dialogResult.newRelativeHeights if not newHeights: return newRelativeInputHeight = newHeights.input if newHeights.input != "empty" else directPortPair.relativeInputHeight newRelativeOutputHeight = (newHeights.output if newHeights.output != "empty" else directPortPair.relativeOutputHeight) directPortPair.setRelativeHeights(newRelativeInputHeight, newRelativeOutputHeight, self.storage.h) newText = self._getDirectPortPairListItemText(directPortPair) selectedItem.setText(newText) def _getFirstSelectedDirectPortPairListWidgetItem( self, ) -> _tp.Optional[QListWidgetItem]: leftSelectedItems = self._leftDirectPortPairsItemListWidget.selectedItems( ) if leftSelectedItems: return leftSelectedItems[0] rightSelectedItems = self._rightDirectPortPairsItemListWidget.selectedItems( ) if rightSelectedItems: return rightSelectedItems[0] return None def incrSize(self): self._changeSize(self.HEIGHT_INCREMENT, self.WIDTH_INCREMENT) def decrSize(self): self._changeSize(-self.HEIGHT_INCREMENT, -self.WIDTH_INCREMENT) def _changeSize(self, deltaH, deltaW): self.storage.updatePortItemPositions(deltaH, deltaW) self.storage.h += deltaH self.storage.w += deltaW self.storage.updateHeatExchangersAfterTankSizeChange() self.storage.updateImage() def acceptedEdit(self): # print("Changing displayName") test = self.le.text() if self.le.text() == "": qmb = QMessageBox() qmb.setText("Please set a name for this storage tank.") qmb.setStandardButtons(QMessageBox.Ok) qmb.setDefaultButton(QMessageBox.Ok) qmb.exec() return self.storage.setName(self.le.text()) self.close() def cancel(self): self.close()
class MainWindow(QMainWindow): """ 主窗口类 """ def __init__(self): super().__init__() #print('init') self.item_cnt = 0 # self.canvas_cnt = 1 # 使用QListWidget来记录已有的图元,并用于选择图元。注:这是图元选择的简单实现方法,更好的实现是在画布中直接用鼠标选择图元 self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(200) # 使用QGraphicsView作为画布 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, 600, 600) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(610, 610) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget # 设置菜单栏 menubar = self.menuBar() file_menu = menubar.addMenu('文件') set_pen_act = file_menu.addAction('设置画笔') reset_canvas_act = file_menu.addAction('重置画布') save_canvas_act = file_menu.addAction('保存画布') exit_act = file_menu.addAction('退出') draw_menu = menubar.addMenu('绘制') line_menu = draw_menu.addMenu('线段') line_naive_act = line_menu.addAction('Naive') line_dda_act = line_menu.addAction('DDA') line_bresenham_act = line_menu.addAction('Bresenham') polygon_menu = draw_menu.addMenu('多边形') polygon_dda_act = polygon_menu.addAction('DDA') polygon_bresenham_act = polygon_menu.addAction('Bresenham') ellipse_act = draw_menu.addAction('椭圆') curve_menu = draw_menu.addMenu('曲线') curve_bezier_act = curve_menu.addAction('Bezier') curve_b_spline_act = curve_menu.addAction('B-spline') edit_menu = menubar.addMenu('编辑') translate_act = edit_menu.addAction('平移') rotate_act = edit_menu.addAction('旋转') scale_act = edit_menu.addAction('缩放') clip_menu = edit_menu.addMenu('裁剪') clip_cohen_sutherland_act = clip_menu.addAction('Cohen-Sutherland') clip_liang_barsky_act = clip_menu.addAction('Liang-Barsky') # 连接信号和槽函数 exit_act.triggered.connect(qApp.quit) set_pen_act.triggered.connect(self.set_pen) reset_canvas_act.triggered.connect(self.reset_canvas) save_canvas_act.triggered.connect(self.save_canvas) line_naive_act.triggered.connect(self.line_naive_action) line_dda_act.triggered.connect(self.line_dda_action) line_bresenham_act.triggered.connect(self.line_bresenham_action) polygon_dda_act.triggered.connect(self.polygon_dda_action) polygon_bresenham_act.triggered.connect(self.polygon_bresenham_action) ellipse_act.triggered.connect(self.ellipse_action) curve_bezier_act.triggered.connect(self.curve_bezier_action) curve_b_spline_act.triggered.connect(self.curve_b_spline_action) translate_act.triggered.connect(self.translate_action) rotate_act.triggered.connect(self.rotate_action) scale_act.triggered.connect(self.scale_action) clip_cohen_sutherland_act.triggered.connect(self.clip_cohen_sutherland_action) clip_liang_barsky_act.triggered.connect(self.clip_liang_barsky_action) self.list_widget.currentTextChanged.connect(self.canvas_widget.selection_changed) # 设置主窗口的布局 self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.statusBar().showMessage('空闲') self.resize(600, 600) self.setWindowTitle('CG 171860004') def get_id(self): self.item_cnt += 1 _id = str(self.item_cnt) return _id def set_pen(self): red, ok1 = QInputDialog.getInt(self, "设置画笔颜色", "RED:") green, ok2 = QInputDialog.getInt(self, "设置画笔颜色", "GREEN:") blue, ok3 = QInputDialog.getInt(self, "设置画笔颜色", "BLUE:") if ok1 and ok2 and ok3: self.canvas_widget.set_color(QColor(red, green, blue)) def reset_canvas(self): width, ok1 = QInputDialog.getInt(self, "重置画布", "width:") height, ok2 = QInputDialog.getInt(self, "重置画布", "height:") if width < 100: width = 100 if width > 1000: width = 1000 if height < 100: height = 100 if height > 1000: height = 1000 if ok1 and ok2: if self.canvas_widget.status != '': self.canvas_widget.finish_draw() self.list_widget.disconnect() self.list_widget.clear() self.scene.clear() self.list_widget.currentTextChanged.connect(self.canvas_widget.selection_changed) self.item_cnt = 0 self.scene.setSceneRect(0, 0, width, height) self.canvas_widget.setFixedSize(width + 10, height + 10) def save_canvas(self): self.list_widget.clearSelection() self.canvas_widget.clear_selection() img = self.canvas_widget.grab() name, ok = QInputDialog.getText(self, "保存画布", "文件名:") if ok: file_name = "output/"+name+".bmp" # self.canvas_cnt = self.canvas_cnt + 1 img.save(file_name) def line_naive_action(self): self.canvas_widget.start_draw_line('Naive', str(self.item_cnt)) self.statusBar().showMessage('Naive算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_dda_action(self): self.canvas_widget.start_draw_line('DDA', str(self.item_cnt)) self.statusBar().showMessage('DDA算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_bresenham_action(self): self.canvas_widget.start_draw_line('Bresenham', str(self.item_cnt)) self.statusBar().showMessage('Bresenham算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_dda_action(self): self.canvas_widget.start_draw_polygon('DDA', str(self.item_cnt)) self.statusBar().showMessage('DDA算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_bresenham_action(self): self.canvas_widget.start_draw_polygon('Bresenham', str(self.item_cnt)) self.statusBar().showMessage('Bresenham算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def ellipse_action(self): self.canvas_widget.start_draw_ellipse(str(self.item_cnt)) self.statusBar().showMessage('中点圆生成算法绘制椭圆') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_b_spline_action(self): self.canvas_widget.start_draw_curve('B_spline', str(self.item_cnt)) self.statusBar().showMessage('B-spline算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_bezier_action(self): self.canvas_widget.start_draw_curve('Bezier', str(self.item_cnt)) self.statusBar().showMessage('Bezier算法绘制曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def translate_action(self): self.canvas_widget.start_translate() self.statusBar().showMessage('平移') def rotate_action(self): self.canvas_widget.start_rotate() self.statusBar().showMessage('旋转') def scale_action(self): self.canvas_widget.start_scale() self.statusBar().showMessage('缩放') def clip_cohen_sutherland_action(self): self.canvas_widget.start_clip('Cohen-Sutherland') self.statusBar().showMessage('Cohen-Sutherland算法裁剪') def clip_liang_barsky_action(self): self.canvas_widget.start_clip('Liang-Barsky') self.statusBar().showMessage('Liang-Barsky算法裁剪')
class Window(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.mainWidget = QWidget() self.mainWidget.setMinimumSize(QtCore.QSize(640, 480)) self.mainWidget.setWindowTitle("menoTulo") self.grid = QVBoxLayout() self.ui() def inputGrid(self): self.inputGrid = QHBoxLayout() self.otsikko = QLineEdit(self) self.summa = QLineEdit(self) self.otsikko.setPlaceholderText("Tulon/menon kuvaus...") self.summa.setPlaceholderText("Summa...") self.inputGrid.addWidget(self.otsikko) self.inputGrid.addWidget(self.summa) self.grid.addLayout(self.inputGrid) def nappiGrid(self): self.nappiGrid = QHBoxLayout() self.tuloButton = QPushButton("Lisää tulo", self) self.menoButton = QPushButton("Lisää meno", self) self.tuloButton.clicked.connect(self.addTulo) self.menoButton.clicked.connect(self.addMeno) self.nappiGrid.addWidget(self.tuloButton) self.nappiGrid.addWidget(self.menoButton) self.grid.addLayout(self.nappiGrid) def listaGrid(self): self.listaGrid = QHBoxLayout() self.tuloLista = QListWidget() self.menoLista = QListWidget() self.tuloLista.itemClicked.connect(self.deleteItem) self.menoLista.itemClicked.connect(self.deleteItem) self.listaGrid.addWidget(self.tuloLista) self.listaGrid.addWidget(self.menoLista) self.grid.addLayout(self.listaGrid) def totalGrid(self): self.totalGrid = QHBoxLayout() self.totalSum = 0 self.totalMenot = 0 self.totalTulot = 0 self.tuloLabel = QLabel() self.sumLabel = QLabel() self.menoLabel = QLabel() self.clearButton = QPushButton("TYHJENNÄ KAIKKI", self) self.clearButton.clicked.connect(self.clearAll) self.totalGrid.addWidget(self.tuloLabel) self.totalGrid.addWidget(self.sumLabel) self.totalGrid.addWidget(self.menoLabel) self.totalGrid.addWidget(self.clearButton) self.tuloLabel.setText("TULOT:") self.sumLabel.setText("TOTAL:") self.menoLabel.setText("MENOT:") self.grid.addLayout(self.totalGrid) def ui(self): self.inputGrid() self.nappiGrid() self.listaGrid() self.totalGrid() self.mainWidget.setLayout(self.grid) self.mainWidget.show() def virhe(self): QMessageBox.about(self, "Virhe", "Syötä summaan vain numeroita!") def poistoVarmistus(self): vastaus = QMessageBox.question(self, "Poista valittu", "Poistetaanko varmasti?", QMessageBox.No | QMessageBox.Yes) if vastaus == QMessageBox.Yes: return True return False def tyhjennysVarmistus(self): vastaus = QMessageBox.question(self, "Tyhjennä kaikki", "Oletko varma?", QMessageBox.No | QMessageBox.Yes) if vastaus == QMessageBox.Yes: return True return False def clearAll(self): if self.tyhjennysVarmistus(): self.totalSum = 0 self.totalMenot = 0 self.totalTulot = 0 self.tuloLista.clear() self.menoLista.clear() self.tuloLabel.setText("TULOT:") self.sumLabel.setText("TOTAL:") self.menoLabel.setText("MENOT:") def deleteItem(self): if self.sender() == self.tuloLista: lista = self.tuloLista else: lista = self.menoLista if not lista.selectedItems(): return if self.poistoVarmistus(): for item in lista.selectedItems(): poistoSumma = int( lista.takeItem(lista.row(item)).text().split()[0]) lista.takeItem(lista.row(item)) if lista == self.tuloLista: self.totalTulot -= poistoSumma else: self.totalMenot -= poistoSumma self.updateTotal() self.tuloLista.clearSelection() self.menoLista.clearSelection() def updateTotal(self): self.tuloLabel.setText("TULOT: " + str(self.totalTulot)) self.menoLabel.setText("MENOT: " + str(self.totalMenot)) self.totalSum = self.totalTulot - self.totalMenot self.sumLabel.setText("TOTAL: " + str(self.totalSum)) def addMeno(self): if not self.summa.text().isdigit(): self.virhe() self.summa.clear() else: infoText = self.summa.text() + " " + self.otsikko.text() if infoText != " ": self.menoLista.addItem(infoText) self.totalSum -= int(self.summa.text()) self.sumLabel.setText("TOTAL: " + str(self.totalSum)) self.totalMenot += int(self.summa.text()) self.menoLabel.setText("MENOT: " + str(self.totalMenot)) self.summa.clear() self.otsikko.clear() else: pass def addTulo(self): if not self.summa.text().isdigit(): self.virhe() self.summa.clear() else: infoText = self.summa.text() + " " + self.otsikko.text() if infoText != " ": self.tuloLista.addItem(infoText) self.totalSum += int(self.summa.text()) self.sumLabel.setText("TOTAL: " + str(self.totalSum)) self.totalTulot += int(self.summa.text()) self.tuloLabel.setText("TULOT: " + str(self.totalTulot)) self.summa.clear() self.otsikko.clear() else: pass
class CreateQuizWindow(QWidget): def __init__(self): super().__init__() self.DEFAULT_BLOCK = (10, 100, [('Вопрос №1', ['Ответ №1', 'Ответ №2'], 0)]) self.blocks = [self.DEFAULT_BLOCK] self.questions = self.blocks[0][2] self.cur_block = 0 self.cur_question = -1 self.initUI() def initUI(self): self.move(*COORDS) self.setWindowTitle('pyQuiz') self.title_edit = QLineEdit() self.title_edit.setPlaceholderText('Название викторины') self.title_edit.setAlignment(Qt.AlignCenter | Qt.AlignHCenter) self.prev_block_button = QPushButton() self.prev_block_button.setIcon(QIcon('assets/back.png')) self.prev_block_button.setFixedWidth(30) self.prev_block_button.setEnabled(False) self.prev_block_button.clicked.connect(self.prevBlock) self.next_block_button = QPushButton() self.next_block_button.setIcon(QIcon('assets/plus.png')) self.next_block_button.setFixedWidth(30) self.next_block_button.clicked.connect(self.nextBlock) self.delete_block_button = QPushButton() self.delete_block_button.setIcon(QIcon('assets/cross.png')) self.delete_block_button.setFixedWidth(30) self.delete_block_button.setEnabled(False) self.delete_block_button.clicked.connect(self.delBlock) self.block_name_label = QLabel('Блок №1') self.block_name_label.setAlignment(Qt.AlignCenter | Qt.AlignHCenter) self.block_time_limit = QLineEdit() self.block_time_limit.setPlaceholderText('Время на вопрос') self.block_points = QLineEdit() self.block_points.setPlaceholderText('Очки за вопрос') self.add_question_button = QPushButton() self.add_question_button.setIcon(QIcon('assets/plus.png')) self.add_question_button.clicked.connect(self.addQuestion) self.delete_question_button = QPushButton() self.delete_question_button.setIcon(QIcon('assets/cross.png')) self.delete_question_button.clicked.connect(self.deleteQuestion) self.questions_list = QListWidget() self.questions_list.setFixedWidth(150) self.questions_list.itemSelectionChanged.connect(self.switchQuestion) self.question_text_edit = QLineEdit() self.question_text_edit.setPlaceholderText('Текст вопроса') self.options_list = QListWidget() self.exit_button = QPushButton() self.exit_button.clicked.connect(self.exit) self.exit_button.setText('Назад') self.exit_button.setStyleSheet( 'QPushButton{font-size:30px;text-align:center;background-color:#3C3F41;color:#BBBBBB;}' ) self.save_button = QPushButton() self.save_button.setText('Сохранить') self.save_button.setStyleSheet( 'QPushButton{font-size:30px;text-align:center;background-color:#3C3F41;color:#BBBBBB;}' ) self.save_button.clicked.connect(self.save) self.add_option_button = QPushButton() self.add_option_button.setIcon(QIcon('assets/plus.png')) self.add_option_button.clicked.connect(self.addOption) self.delete_option_button = QPushButton() self.delete_option_button.setIcon(QIcon('assets/cross.png')) self.delete_option_button.clicked.connect(self.delOption) vbox = QVBoxLayout() menu_layout = QHBoxLayout() menu_layout.addWidget(self.prev_block_button) menu_layout.addWidget(self.block_name_label) menu_layout.addWidget(self.delete_block_button) menu_layout.addWidget(self.next_block_button) block_settings = QHBoxLayout() block_settings.addWidget(self.block_time_limit) block_settings.addWidget(self.block_points) main_layout = QHBoxLayout() block_table_layout = QVBoxLayout() block_table_layout.addWidget(self.questions_list) change_block_layout = QHBoxLayout() change_block_layout.addWidget(self.add_question_button) change_block_layout.addWidget(self.delete_question_button) block_table_layout.addLayout(change_block_layout) main_layout.addLayout(block_table_layout) question_layout = QVBoxLayout() question_layout.addWidget(self.question_text_edit) question_layout.addWidget(self.options_list) change_question_layout = QHBoxLayout() change_question_layout.addWidget(self.add_option_button) change_question_layout.addWidget(self.delete_option_button) question_layout.addLayout(change_question_layout) main_layout.addLayout(question_layout) footer_menu = QHBoxLayout() footer_menu.addWidget(self.exit_button) footer_menu.addWidget(self.save_button) vbox.addWidget(self.title_edit) vbox.addLayout(menu_layout) vbox.addLayout(block_settings) vbox.addLayout(main_layout) vbox.addLayout(footer_menu) self.restoreBlock() self.setLayout(vbox) self.setStyleSheet(QUIZ_STYLE_SHEET) self.setFixedSize(500, 500) def blockNavUpdate(self): if self.cur_block == len(self.blocks) - 1: self.next_block_button.setIcon(QIcon('assets/plus.png')) else: self.next_block_button.setIcon(QIcon('assets/next.png')) self.prev_block_button.setEnabled(self.cur_block > 0) self.next_block_button.setEnabled( self.cur_block < QUIZ_BLOCKS_NUM_RANGE.stop - 1) self.delete_block_button.setEnabled(len(self.blocks) > 1) self.block_name_label.setText(f'Блок № {self.cur_block + 1}') def showMsg(self, s, type=QMessageBox.Critical): msg = QMessageBox() msg.setIcon(type) msg.setText(s) msg.setWindowTitle('pyQuiz') msg.exec_() def saveBlock(self): if self.cur_question != -1: if not self.saveQuestion(): return False time = self.block_time_limit.text() points = self.block_points.text() if not time.isnumeric(): self.showMsg('Время на вопрос должно быть целым числом!') return False time = int(time) if time not in QUIZ_BLOCK_TIME_RANGE: self.showMsg( f'Время на вопрос должно быть не меньше {QUIZ_BLOCK_TIME_RANGE.start}, ' f'но меньше {QUIZ_BLOCK_TIME_RANGE.stop}') return False if not points.isnumeric(): self.showMsg('Кол-во очков за вопрос должно быть целым числом!') return False points = int(points) if points not in QUIZ_BLOCK_POINTS_RANGE: self.showMsg( f'Кол-во очков за вопрос должно быть не меньше {QUIZ_BLOCK_POINTS_RANGE.start}, ' f'но меньше {QUIZ_BLOCK_POINTS_RANGE.stop}') return False if len(self.questions) not in QUIZ_QUESTIONS_NUM_RANGE: self.showMsg( f'Кол-во вопросов в блоке должно быть не меньше {QUIZ_QUESTIONS_NUM_RANGE.start}, ' f'но меньше {QUIZ_QUESTIONS_NUM_RANGE.stop}') return False self.blocks[self.cur_block] = (time, points, self.questions) return True def restoreBlock(self): block = self.blocks[self.cur_block] self.block_time_limit.setText(str(block[0])) self.block_points.setText(str(block[1])) self.questions_list.clear() for question_text, options, right_option in block[2]: self.questions_list.addItem(question_text) self.questions = block[2][:] self.cur_question = -1 self.questions_list.clearSelection() self.question_text_edit.setText('') self.options_list.clear() self.cur_question = -1 def nextBlock(self): if not self.saveBlock(): return self.cur_block += 1 if self.cur_block == len(self.blocks): self.blocks.append(self.DEFAULT_BLOCK) self.blockNavUpdate() self.restoreBlock() def prevBlock(self): if not self.saveBlock(): return self.cur_block -= 1 self.blockNavUpdate() self.restoreBlock() def delBlock(self): self.blocks.pop(self.cur_block) if self.cur_block == len(self.blocks): self.cur_block -= 1 self.blockNavUpdate() self.restoreBlock() def switchQuestion(self): if len(self.questions_list.selectedIndexes()) == 0: return if self.cur_question != -1: if not self.saveQuestion(): self.questions_list.blockSignals(True) self.questions_list.clearSelection() self.questions_list.item(self.cur_question).setSelected(True) self.questions_list.blockSignals(False) return self.cur_question = self.questions_list.selectedIndexes()[0].row() self.restoreQuestion() def addQuestion(self): question_text, okBtnPressed = QInputDialog.getText( self, 'Создание вопроса', 'Введите текст вопроса') if not okBtnPressed: return if len(question_text) not in QUIZ_QUESTION_TEXT_LEN_RANGE: self.showMsg( f'Длина вопроса должна быть не меньше {QUIZ_QUESTION_TEXT_LEN_RANGE.start}, ' f'но меньше {QUIZ_QUESTION_TEXT_LEN_RANGE.stop}', QMessageBox.Critical) return i = -1 if len(self.questions_list.selectedIndexes()) > 0: i = self.questions_list.selectedIndexes()[0].row() i += 1 self.questions.insert(i, (question_text, ['Ответ №1', 'Ответ №2'], 0)) self.questions_list.insertItem(i, question_text) self.delete_question_button.setEnabled(True) def deleteQuestion(self): if len(self.questions_list.selectedIndexes()) == 0: return i = self.questions_list.selectedIndexes()[0].row() self.questions_list.takeItem(i) self.questions.pop() self.cur_question = -1 def saveQuestion(self): question_text = self.question_text_edit.text() if len(question_text) not in QUIZ_QUESTION_TEXT_LEN_RANGE: self.showMsg( f'Длина текста вопроса должна быть не меньше {QUIZ_QUESTION_TEXT_LEN_RANGE.start}, ' f'но меньше {QUIZ_QUESTION_TEXT_LEN_RANGE.stop}', QMessageBox.Critical) return False self.questions_list.item(self.cur_question).setText(question_text) options = [] for i in range(len(self.options_list)): options.append(self.options_list.item(i).text()) if len(options) not in QUIZ_OPTIONS_NUM_RANGE: self.showMsg( f'Кол-во ответов на вопрос должно быть не меньше {QUIZ_OPTIONS_NUM_RANGE.start}, ' f'но меньше {QUIZ_OPTIONS_NUM_RANGE.stop}', QMessageBox.Critical) return False if len(self.options_list.selectedIndexes()) != 1: self.showMsg(f'Не выбран правильный ответ!', QMessageBox.Critical) return False right_answer = self.options_list.selectedIndexes()[0].row() self.questions[self.cur_question] = (question_text, options, right_answer) return True def restoreQuestion(self): question = self.questions[self.cur_question] self.question_text_edit.setText(question[0]) self.options_list.clear() for option in question[1]: self.options_list.addItem(option) self.options_list.item(question[2]).setSelected(True) def addOption(self): option_text, okBtnPressed = QInputDialog.getText( self, 'Создание ответа', 'Введите текст ответа') if not okBtnPressed: return if len(option_text) not in QUIZ_OPTION_TEXT_LEN_RANGE: self.showMsg( f'Длина ответа должна быть не меньше {QUIZ_OPTION_TEXT_LEN_RANGE.start}, ' f'но меньше {QUIZ_OPTION_TEXT_LEN_RANGE.stop}', QMessageBox.Critical) return self.options_list.addItem(option_text) def delOption(self): for index in self.options_list.selectedIndexes(): self.options_list.takeItem(index.row()) def save(self): if not self.saveBlock(): return quiz_name = self.title_edit.text() if len(quiz_name) not in QUIZ_NAME_LEN_RANGE: self.showMsg( f'Длина названия викторины должна быть не меньше {QUIZ_NAME_LEN_RANGE.start}, ' f'но меньше {QUIZ_NAME_LEN_RANGE.stop}') return quiz_json = {'name': quiz_name} cur = 0 for time, pts, questions in self.blocks: for text, options, right_answer in questions: quiz_json[str(cur)] = { 'time': time, 'score': pts, 'question': text, 'answers': list(options), 'true': right_answer } cur += 1 # QUIZ_SAVE_DIR file_name_preffix = secrets.token_hex(5) while os.path.isfile(os.path.join(QUIZ_SAVE_DIR, file_name_preffix)): file_name_preffix = secrets.token_hex(5) # https://stackoverflow.com/questions/18337407/saving-utf-8-texts-in-json-dumps-as-utf8-not-as-u-escape-sequence file_path = os.path.join(QUIZ_SAVE_DIR, quiz_name + '_' + file_name_preffix + '.json') with open(file_path, 'w', encoding='utf8') as json_file: json.dump(quiz_json, json_file, ensure_ascii=False, sort_keys=True, indent=4) self.exit() def exit(self): global COORDS COORDS = [self.x(), self.y()] self.create_game_window = QuizSelectionWindow() self.create_game_window.show() self.close()
class FontDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.__font_db = QFontDatabase() self.setWindowTitle(_("Select font")) layout = QVBoxLayout() content_layout = QHBoxLayout() font_list_layout = QVBoxLayout() font_list_layout.addWidget(QLabel(_("Font family:"))) self.__font_search = UpDownPassingLineEdit() self.__font_search.textEdited.connect(self.__family_search_edited) font_list_layout.addWidget(self.__font_search) self.__font_list = QListWidget() self.__font_list.setFocusPolicy(Qt.NoFocus) for font in sorted(self.__font_db.families()): if self.__font_db.isSmoothlyScalable(font): self.__font_list.addItem(font) self.__font_list.currentTextChanged.connect(self.__family_changed) self.__font_search.destination_widget = self.__font_list font_list_layout.addWidget(self.__font_list) content_layout.addLayout(font_list_layout) style_layout = QVBoxLayout() style_layout.setAlignment(Qt.AlignCenter) self.__bold_widget = self.__create_style_button( "B", _("Bold"), QKeySequence.Bold, FontStyle.bold) style_layout.addWidget(self.__bold_widget) self.__italic_widget = self.__create_style_button( "I", _("Italic"), QKeySequence.Italic, FontStyle.italic) style_layout.addWidget(self.__italic_widget) self.__underline_widget = self.__create_style_button( "U", _("Underline"), QKeySequence.Underline, FontStyle.underline) style_layout.addWidget(self.__underline_widget) self.__strike_widget = self.__create_style_button( "S", _("Strike Out"), STRIKE_OUT, FontStyle.strike) style_layout.addWidget(self.__strike_widget) content_layout.addLayout(style_layout) size_layout = QVBoxLayout() size_layout.setAlignment(Qt.AlignHCenter) size_layout.addWidget(QLabel(_("Size (px):"))) self.__size_edit = UpDownPassingLineEdit() self.__size_edit.textEdited.connect(self.__size_edited) self.__size_edit.setValidator(QIntValidator(5, 60)) size_layout.addWidget(self.__size_edit) self.__size_edit.setSizePolicy( QSizePolicy.Preferred, self.__size_edit.sizePolicy().verticalPolicy()) self.__size_slider = QSlider(Qt.Vertical) self.__size_slider.setFocusPolicy(Qt.NoFocus) self.__size_slider.setRange(5, 60) self.__size_slider.setTickInterval(5) self.__size_slider.setTickPosition(QSlider.TicksRight) self.__size_slider.valueChanged.connect(self.__size_changed) self.__size_edit.destination_widget = self.__size_slider size_layout.addWidget(self.__size_slider) content_layout.addLayout(size_layout) layout.addLayout(content_layout) self.__example = QLineEdit("AaBbYyZz") self.__example.setFixedHeight(80) layout.addWidget(self.__example) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.button(QDialogButtonBox.Ok).setText(_("Ok")) button_box.button(QDialogButtonBox.Cancel).setText(_("Cancel")) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) layout.addWidget(button_box) self.setLayout(layout) self.__font = Fonts.default def __create_style_button(self, text, tooltip, keys, font_style): def toggled(value): self.__font = self.__font.change(font_style, value) self.__refresh_example_font() shortcut = QKeySequence(keys) widget = QPushButton(text) widget.setShortcut(shortcut) widget.setToolTip("{0} ({1})".format(tooltip, shortcut.toString())) font = widget.font() if font_style == FontStyle.bold: font.setBold(True) elif font_style == FontStyle.italic: font.setItalic(True) elif font_style == FontStyle.underline: font.setUnderline(True) elif font_style == FontStyle.strike: font.setStrikeOut(True) widget.setFont(font) widget.setCheckable(True) widget.toggled.connect(toggled) return widget @property def ufl_font(self): return self.__font @ufl_font.setter def ufl_font(self, value): self.__font = value for item in self.__font_list.findItems(value.family, Qt.MatchExactly): self.__font_list.setCurrentItem(item) break else: self.__font_list.clearSelection() self.__font_search.setText(value.family) self.__bold_widget.setChecked(FontStyle.bold in value.style) self.__italic_widget.setChecked(FontStyle.italic in value.style) self.__underline_widget.setChecked(FontStyle.underline in value.style) self.__strike_widget.setChecked(FontStyle.strike in value.style) self.__size_slider.setValue(value.size) self.__size_edit.setText(str(value.size)) self.__refresh_example_font() def __refresh_example_font(self): qfont = QFont(self.__font.family) qfont.setPixelSize(self.__font.size) qfont.setBold(FontStyle.bold in self.__font.style) qfont.setItalic(FontStyle.italic in self.__font.style) qfont.setStrikeOut(FontStyle.strike in self.__font.style) qfont.setUnderline(FontStyle.underline in self.__font.style) self.__example.setFont(qfont) def __family_search_edited(self, name): selected_text = self.__font_list.currentItem().text() if selected_text.startswith(name): return for item in self.__font_list.findItems(name, Qt.MatchStartsWith): self.__font_list.setCurrentItem(item) break def __family_changed(self, value): if self.__font.family != value: if not self.__font_search.hasFocus(): self.__font_search.setText(value) self.__font = self.__font.change_family(value) self.__refresh_example_font() def __size_edited(self, value): if not value or int(value) < 5: return int_value = int(value) if self.__font.size != int_value: self.__font = self.__font.change_size(int_value) self.__size_slider.setValue(int_value) self.__refresh_example_font() def __size_changed(self, value): if self.__font.size != value: if self.__size_edit.text() != str(value): self.__size_edit.setText(str(value)) self.__size_edit.selectAll() self.__font = self.__font.change_size(value) self.__refresh_example_font()
class SessionViewer(QWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setMinimumSize(1100, 400) main_layout = QHBoxLayout() main_layout.setSpacing(5) self.setLayout(main_layout) list_column = QVBoxLayout() main_layout.addLayout(list_column, stretch=1) refresh_button = QPushButton('🔄 Refresh') refresh_button.setFont(cn.EMOJI_FONT) refresh_button.clicked.connect(self.refresh_list) list_column.addWidget(refresh_button) self.session_list = QListWidget() self.session_list.currentItemChanged.connect(self.show_session) list_column.addWidget(self.session_list) self.refresh_list() self.info_column = QVBoxLayout() main_layout.addLayout(self.info_column, stretch=1) self.info_column.addWidget(QLabel('Selected session info:')) self.session_info_table = QTableWidget() self.session_info_table.setColumnCount(2) self.session_info_table.horizontalHeader().setStretchLastSection(True) self.info_column.addWidget(self.session_info_table) self.info_column.addWidget(QLabel('Contained gestures:')) self.gesture_list = QListWidget() self.gesture_list.currentItemChanged.connect(self.show_gesture) self.info_column.addWidget(self.gesture_list) self.data_column = QVBoxLayout() self.data_column.setContentsMargins(0, 0, 0, 0) main_layout.addLayout(VerticalScrollableExtension( self.data_column, direction=QBoxLayout.TopToBottom, scrolled_spacing=10), stretch=3) self.current_session_data = None self.current_gesture_names = None @staticmethod def load_session_data(session_name): file = f'data/gestures/{session_name}' with open(file, 'rb') as f: data = pickle.load(f) gesture_keys = [ item for item in data if type(data[item]) == np.ndarray ] return data, sorted(gesture_keys, key=lambda x: str(x).zfill(5)) @staticmethod def load_session_list(): return sorted( filter( lambda x: x.startswith('s-') and x.count(cn.FILE_NAME_SEPARATOR ) == 2, os.listdir(cn.DATA_FOLDER))) def refresh_list(self): self.session_list.clear() self.session_list.addItems(SessionViewer.load_session_list()) def cleanup_shown_session(self): self.session_info_table.clear() self.gesture_list.clearSelection() self.gesture_list.clear() for i in range(self.data_column.count()): self.data_column.itemAt(i).widget().plot_data(None) def show_session(self, current_item): if not current_item: return self.cleanup_shown_session() session_name = current_item.text() self.current_session_data, self.current_gesture_names = SessionViewer.load_session_data( session_name) self.session_info_table.setRowCount(2 + len(self.current_session_data) - len(self.current_gesture_names)) row = 0 for info in self.current_session_data: if info in self.current_gesture_names: continue self.session_info_table.setItem(row, 0, QTableWidgetItem(info)) self.session_info_table.setItem( row, 1, QTableWidgetItem(str(self.current_session_data[info]))) row += 1 self.session_info_table.setItem(row, 0, QTableWidgetItem('Gestures')) self.session_info_table.setItem( row, 1, QTableWidgetItem(str(len(self.current_gesture_names)))) self.session_info_table.setItem(row + 1, 0, QTableWidgetItem('Instances')) self.session_info_table.setItem( row + 1, 1, QTableWidgetItem( str( sum( map(lambda name: len(self.current_session_data[name]), self.current_gesture_names))))) self.gesture_list.addItems( map( lambda name: f'{name} ({len(self.current_session_data[name])} inst.)', self.current_gesture_names)) def show_gesture(self): current_gesture_data = self.current_session_data[ self.current_gesture_names[self.gesture_list.currentIndex().row()]] new_count = len(current_gesture_data) old_count = self.data_column.count() for i in range(old_count, new_count): self.data_column.addWidget(StaticSignalWidget()) for i in range(new_count, old_count): self.data_column.itemAt(i).widget().plot_data(None) for i, instance in enumerate(current_gesture_data): # We use a function here, to create a temporary scope for index & data w = self.data_column.itemAt(i).widget() w.plot_data(instance)
class MainWindow(QMainWindow): """ 主窗口类 """ def __init__(self): super().__init__() self.item_cnt = 0 # 使用QListWidget来记录已有的图元,并用于选择图元。注:这是图元选择的简单实现方法,更好的实现是在画布中直接用鼠标选择图元 self.list_widget = QListWidget(self) self.list_widget.setMinimumWidth(200) # 使用QGraphicsView作为画布 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, 600, 600) self.canvas_widget = MyCanvas(self.scene, self) self.canvas_widget.setFixedSize(600 + 2, 600 + 2) self.canvas_widget.main_window = self self.canvas_widget.list_widget = self.list_widget self.canvas_widget.temp_id = self.get_id() # 设置菜单栏 menubar = self.menuBar() file_menu = menubar.addMenu('文件') # set_pen_act = file_menu.addAction('设置画笔') set_pen_act = file_menu.addMenu('设置画笔') set_pen_color_act = set_pen_act.addAction('设置颜色') set_pen_width_act = set_pen_act.addAction('设置粗细') reset_canvas_act = file_menu.addAction('重置画布') save_canvas_act = file_menu.addAction('保存画布') exit_act = file_menu.addAction('退出') draw_menu = menubar.addMenu('绘制') pencil_act = draw_menu.addAction('铅笔') line_menu = draw_menu.addMenu('线段') line_naive_act = line_menu.addAction('Naive') line_dda_act = line_menu.addAction('DDA') line_bresenham_act = line_menu.addAction('Bresenham') polygon_menu = draw_menu.addMenu('多边形') polygon_dda_act = polygon_menu.addAction('DDA') polygon_bresenham_act = polygon_menu.addAction('Bresenham') ellipse_act = draw_menu.addAction('椭圆') curve_menu = draw_menu.addMenu('曲线') curve_bezier_act = curve_menu.addAction('Bezier') curve_b_spline_act = curve_menu.addAction('B-spline') polyline_menu = draw_menu.addMenu('折线') polyline_dda_act = polyline_menu.addAction('DDA') polyline_bresenham_act = polyline_menu.addAction('Bresenham') edit_menu = menubar.addMenu('编辑') translate_act = edit_menu.addAction('平移') rotate_act = edit_menu.addAction('旋转') scale_act = edit_menu.addAction('缩放') clip_menu = edit_menu.addMenu('裁剪') clip_cohen_sutherland_act = clip_menu.addAction('Cohen-Sutherland') clip_liang_barsky_act = clip_menu.addAction('Liang-Barsky') copy_act = edit_menu.addAction('复制粘贴') delete_act = edit_menu.addAction('删除') choose_act = menubar.addAction('鼠标选中') undo_act = menubar.addAction('撤销') # 连接信号和槽函数 mark # set_pen_act.triggered.connect(self.set_pen_color_action) set_pen_color_act.triggered.connect(self.set_pen_color_action) set_pen_width_act.triggered.connect(self.set_pen_width_action) reset_canvas_act.triggered.connect(self.reset_canvas_action) save_canvas_act.triggered.connect(self.save_canvas_action) undo_act.triggered.connect(self.undo_action) exit_act.triggered.connect(qApp.quit) pencil_act.triggered.connect(self.pencil_action) line_naive_act.triggered.connect(self.line_naive_action) line_dda_act.triggered.connect(self.line_dda_action) line_bresenham_act.triggered.connect(self.line_bresenham_action) polygon_dda_act.triggered.connect(self.polygon_dda_action) polygon_bresenham_act.triggered.connect(self.polygon_bresenham_action) polyline_dda_act.triggered.connect(self.polyline_dda_action) polyline_bresenham_act.triggered.connect( self.polyline_bresenham_action) ellipse_act.triggered.connect(self.ellipse_action) curve_bezier_act.triggered.connect(self.curve_bezier_action) curve_b_spline_act.triggered.connect(self.curve_b_spline_action) translate_act.triggered.connect(self.translate_action) rotate_act.triggered.connect(self.rotate_action) scale_act.triggered.connect(self.scale_action) copy_act.triggered.connect(self.copy_action) delete_act.triggered.connect(self.delete_action) clip_liang_barsky_act.triggered.connect(self.clip_liang_barsky_action) clip_cohen_sutherland_act.triggered.connect( self.clip_cohen_sutherland_action) choose_act.triggered.connect(self.choose_action) self.list_widget.currentTextChanged.connect( self.canvas_widget.selection_changed) # 设置主窗口的布局 self.hbox_layout = QHBoxLayout() self.hbox_layout.addWidget(self.canvas_widget) self.hbox_layout.addWidget(self.list_widget, stretch=1) self.central_widget = QWidget() self.central_widget.setLayout(self.hbox_layout) self.setCentralWidget(self.central_widget) self.statusBar().showMessage('空闲') self.resize(600, 600) self.setWindowTitle('XY\'s CG Demo') def get_id(self): _id = str(self.item_cnt) self.item_cnt += 1 return _id def delete_id(self): self.item_cnt -= 1 def undo_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_undo() self.statusBar().showMessage('撤销') def set_pen_color_action(self): if self.canvas_widget.drawing != 0: return color = QColorDialog.getColor() if color.isValid: # 通过isValid()可以判断用户选择的颜色是否有效,若用户选择取消,isValid()将返回false self.canvas_widget.pen_color = color def set_pen_width_action(self): if self.canvas_widget.drawing != 0: return num1, ok1 = QInputDialog.getInt(self, '设置画笔宽度', '输入您的宽度(1~10)', 1, 1, 10, 1) if ok1: # 通过isValid()可以判断用户选择的颜色是否有效,若用户选择取消,isValid()将返回false self.canvas_widget.pen_width = num1 def choose_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.status = '' def reset_canvas_action(self): if self.canvas_widget.drawing != 0: return num1, ok1 = QInputDialog.getInt(self, '获取宽度', '输入您的宽度(100~1000)', 600, 100, 1000, 1) if ok1: num2, ok2 = QInputDialog.getInt(self, '获取高度', '输入您的高度(100~1000)', 600, 100, 1000, 1) if ok1 and ok2: # 清空画布 self.list_widget.clear() self.item_cnt = 0 self.canvas_widget.action_stack = [] self.canvas_widget.temp_id = self.get_id() # 更改画布大小 self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, num1, num2) self.canvas_widget.setScene(self.scene) self.canvas_widget.setFixedSize(num1 + 2, num2 + 2) def save_canvas_action(self): if self.canvas_widget.drawing != 0: return options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog filename, ok = QFileDialog.getSaveFileName( self, "QFileDialog.getSaveFileName()", "", "Pictures (*.bmp)", options=options) if ok: filename = filename + ".bmp" # print(filename) pix_map = self.canvas_widget.grab( self.canvas_widget.sceneRect().toRect()) pix_map.save(filename) def line_naive_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_draw_line('Naive') self.statusBar().showMessage('Naive算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_dda_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_draw_line('DDA') self.statusBar().showMessage('DDA算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def line_bresenham_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_draw_line('Bresenham') self.statusBar().showMessage('Bresenham算法绘制线段') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_dda_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_draw_polygon('DDA') self.statusBar().showMessage('DDA算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polygon_bresenham_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_draw_polygon('Bresenham') self.statusBar().showMessage('Bresenham算法绘制多边形') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polyline_dda_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_draw_polyline('DDA') self.statusBar().showMessage('DDA算法绘制折线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def polyline_bresenham_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_draw_polyline('Bresenham') self.statusBar().showMessage('Bresenham算法绘制折线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def pencil_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_pencil('Pencil') self.statusBar().showMessage('铅笔') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def ellipse_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_draw_ellipse('center') self.statusBar().showMessage('中心圆生成算法绘制椭圆') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_bezier_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_draw_curve('Bezier') self.statusBar().showMessage('绘制Bezier曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def curve_b_spline_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_draw_curve('B-spline') self.statusBar().showMessage('绘制B-spline曲线') self.list_widget.clearSelection() self.canvas_widget.clear_selection() def copy_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_copy() self.statusBar().showMessage('复制粘贴') def translate_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_translate('平移') self.statusBar().showMessage('平移') def rotate_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_rotate('旋转') self.statusBar().showMessage('旋转') def scale_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_scale('缩放') self.statusBar().showMessage('缩放') def delete_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_delete() self.statusBar().showMessage('删除') def clip_liang_barsky_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_clip('Liang-Barsky') self.statusBar().showMessage('线段裁剪') def clip_cohen_sutherland_action(self): if self.canvas_widget.drawing != 0: return self.canvas_widget.start_clip('Cohen-Sutherland') self.statusBar().showMessage('线段裁剪')