--- a +++ b/src/style.kv @@ -0,0 +1,691 @@ +#:kivy 1.10.0 +#:import color src.color +#:import util src.util + +<Widget>: + font_name: 'Roboto-Bold' + font_size: 16 + +<Spacer@Widget>: + size_hint: 1, 1 + +<HSpacer@Widget>: + size_hint: 1, None + +<VSpacer@Widget>: + size_hint: None, 1 + +<HDivider@Widget>: + size_hint: 1, None + height: 3 + + canvas.before: + Color: + rgba: 0, 0, 0, .08 + Rectangle: + pos: self.pos + size: self.size + +<LeftLabel@Label>: + font_name: 'Roboto' + size_hint: None, None + color: .4, .4, .4, 1 + halign: 'left' + + on_texture_size: + self.width = 40 + self.texture.width + self.height = self.texture.height + +<TextButton@Button>: + background_normal: '' + background_color: 0, 0, 0, 0 + + size_hint: None, None + bold: True + color: color.B4 if self.state == 'normal' else color.B3 + + halign: 'left' + + canvas.before: + Color: + rgba: (0, 0, 0, .01) if self.state != 'normal' else (0, 0, 0, 0) + RoundedRectangle: + size: self.size + pos: self.pos + + on_texture_size: + self.width = 40 + self.texture.width + self.height = self.texture.height + + +<Card@BoxLayout>: + size_hint: None, None + + orientation: 'vertical' + + background_color: 0, 0, 0, 0 + bg_color: 1, 1, 1, 1 + + radius: 20 + canvas.before: + Rectangle: + size: self.size[0] + 2 * self.radius, self.size[1] + 2 * self.radius + pos: self.pos[0] - self.radius, self.pos[1] - 1.5 * self.radius + texture: color.createBoxShadow(self.size[0] + self.radius, self.size[1] + self.radius, 0.8 * self.radius, 0.4) + + Color: + rgba: self.bg_color + Rectangle: + size: self.size + pos: self.pos + +<Chip>: + size_hint: None, None + height: self.minimum_height + width: self.minimum_width + + radius: 5 + + selected: True + text: '' + + canvas.before: + Rectangle: + size: self.size[0] + 2 * self.radius, self.size[1] + 2 * self.radius + pos: self.pos[0] - self.radius, self.pos[1] - 1.5 * self.radius + texture: color.createBoxShadow(self.size[0] + self.radius, self.size[1] + self.radius, self.radius, 0.3) + Color: + rgb: (.85, .85, .85) if self.selected else (.95, .95, .95) + RoundedRectangle: + size: self.size + pos: self.pos + radius: [4] + +<ChipInput>: + padding: [10, 0, 5, 0] + spacing: 0 + + hint_text: '' + TextInput: + id: input + background: '' + background_color: 0, 0, 0, 0 + + multiline: False + + font_name: 'Roboto' + size_hint: None, None + size: 80, self.minimum_height - 2 + + hint_text: self.hint_text + + on_text_validate: + root.submit() + on_text: + self.width = max(80, 20 + self._lines_labels[0].size[0] if self._lines_labels else 0) + canvas.before: + Color: + rgb: .4, .4, .4 + +<ChipInputAdder>: + padding: [10, 0, 5, 0] + spacing: 0 + + hint_text: '' + + TextInput: + id: input + background: '' + background_color: 0, 0, 0, 0 + + multiline: False + + font_name: 'Roboto' + size_hint: None, None + size: 80, self.minimum_height - 2 + + hint_text: self.hint_text + + on_text_validate: + root.submit() + on_text: + self.width = max(80, 20 + self._lines_labels[0].size[0] if self._lines_labels else 0) + canvas.before: + Color: + rgb: .4, .4, .4 + + Button: + size_hint: None, 1 + width: self.height + + background_normal: '' + background_color: 0, 0, 0, 0 + + canvas.before: + Color: + rgba: (.6, .6, .6, 1) if self.state == 'normal' else (.5, .5, .5, 1) + Line: + points: [self.pos[0] + self.height * .3, self.pos[1] + self.height * .5, self.pos[0] + self.height * .7, self.pos[1] + self.height * .5] + width: self.height / 20 + cap: 'square' + Line: + points: [self.pos[0] + self.height * .5, self.pos[1] + self.height * .3, self.pos[0] + self.height * .5, self.pos[1] + self.height * .7] + width: self.height / 20 + cap: 'square' + on_press: + root.submit() + +<ChipRemovable>: + selected: False + + padding: [10, 5, 5, 5] + spacing: 10 + + Label: + font_name: 'Roboto' + size_hint: None, None + color: .4, .4, .4, 1 + halign: 'left' + + text: root.text + + on_texture_size: + self.size = self.texture.size + + Button: + id: btn_remove + + size_hint: None, 1 + width: self.height + + background_normal: '' + background_color: 0, 0, 0, 0 + + canvas.before: + Color: + rgba: (.6, .6, .6, 1) if self.state == 'normal' else (.5, .5, .5, 1) + Line: + points: [self.pos[0] + self.height * .3, self.pos[1] + self.height * .3, self.pos[0] + self.height * .7, self.pos[1] + self.height * .7] + width: self.height / 20 + Line: + points: [self.pos[0] + self.height * .3, self.pos[1] + self.height * .7, self.pos[0] + self.height * .7, self.pos[1] + self.height * .3] + width: self.height / 20 + + on_press: + root.remove() + + +<Body@Card>: + orientation: 'vertical' + size_hint: 1, None + pos_hint: { 'center_x': .5, 'center_y': .5 } + + padding: 20 + spacing: 20 + +<HPage@ScrollView>: + do_scroll_x: False + do_scroll_y: True +<HPageBox@BoxLayout>: + orientation: 'vertical' + size_hint: 1, None + height: self.minimum_height + padding: 20 +<HPageBody@Body>: + height: self.minimum_height +<HTitle@Label>: + size_hint: 1, None + height: 40 + color: .4, .4, .4, 1 + font_name: 'RobotoMono-Regular' +<HStack@StackLayout>: + size_hint: 1, None + height: self.minimum_height + spacing: 10 + + +<PopupFileLoader>: + size_hint: None, None + size: 500, 500 + + title: '' + title_color: .4, .4, .4, 1 + + background: '' + background_color: 0, 0, 0, 0 + separator_height: 0 + + radius: 10 + file_path: None + + canvas.before: + Color: + rgba: 1, 1, 1, 1 + Rectangle: + size: self.size[0] + 2 * self.radius, self.size[1] + 2 * self.radius + pos: self.pos[0] - self.radius, self.pos[1] - 2 * self.radius + texture: color.createBoxShadow(self.size[0] + self.radius, self.size[1] + self.radius, self.radius, 0.4) + + BoxLayout: + orientation: 'vertical' + FileChooserListView: + id: file_chooser + color: 0, 0, 0, 1 + + dirselect: True + path: util.samples_dir + + on_selection: + root.selectFile(args[1]) + on_submit: + root.submitFile() + BoxLayout: + size_hint: 1, None + height: self.minimum_height + 10 + + TextButton: + text: 'SELECT' + on_press: + root.submitFile() + TextButton: + text: 'CLOSE' + on_press: + root.dismiss() + +<FileChooserListView>: + layout: layout + FileChooserListLayout: + id: layout + controller: root + +[FileListEntry@FloatLayout+TreeViewNode]: + locked: False + entries: [] + path: ctx.path + is_selected: self.path in ctx.controller().selection + + orientation: 'horizontal' + size_hint_y: None + height: '48dp' if dp(1) > 1 else '24dp' + is_leaf: not ctx.isdir or ctx.name.endswith('..' + ctx.sep) or self.locked + on_touch_down: self.collide_point(*args[1].pos) and ctx.controller().entry_touched(self, args[1]) + on_touch_up: self.collide_point(*args[1].pos) and ctx.controller().entry_released(self, args[1]) + BoxLayout: + pos: root.pos + size_hint_x: None + width: root.width - dp(10) + + Label: + color: .4, .4, .4, 1 + id: filename + text_size: self.width, None + halign: 'left' + shorten: True + text: ctx.name + Label: + color: .4, .4, .4, 1 + text_size: self.width, None + size_hint_x: None + halign: 'right' + text: '{}'.format(ctx.get_nice_size()) + + +<ScreenCard@Card>: + width: min(600, self.parent.width - 40) + + padding: 20 + spacing: 8 + +<StartScreenLabel@Label>: + size_hint: None, None + bold: True + color: .4, .4, .4, 1 + + halign: 'left' + + on_texture_size: + self.width = 40 + (self.texture.width if self.text else 0) + self.height = self.texture.height + +<SpaceStart>: + HPage: + HPageBox: + HPageBody: + pos_hint: { 'center_x': .5, 'center_y': .5 } + + size_hint: None, None + width: min(900, 0.8 * root.width) + height: max(0.9 * root.height, self.minimum_height) + + HTitle: + font_size: 40 + text: 'APOS' + HTitle: + font_name: 'Roboto' + color: color.B3 + font_size: 20 + bold: True + text: 'Advanced Pathology OS' + + ScreenCard: + height: 130 + StartScreenLabel: + text: 'START' + + HSpacer: + height: 3 + TextButton: + text: 'Create slide' + on_press: + app.root.ids.workspace.transition.direction = 'left' + app.root.ids.workspace.current = 'screen_createslide' + TextButton: + text: 'Analyze slide' + on_press: + app.root.ids.workspace.transition.direction = 'left' + app.root.ids.workspace.current = 'screen_analyze' + Spacer: + + ScreenCard: + height: 130 + StartScreenLabel: + text: 'TRAIN' + HSpacer: + height: 3 + TextButton: + text: 'Create disease filter' + on_press: + app.root.ids.workspace.transition.direction = 'right' + app.root.ids.workspace.current = 'screen_train' + TextButton: + text: 'Categorize training data' + on_press: + app.root.ids.workspace.transition.direction = 'right' + app.root.ids.workspace.current = 'screen_categorize' + Spacer: + + ScreenCard: + height: 100 + + StartScreenLabel: + text: 'MISCELLANEOUS' + HSpacer: + height: 3 + TextButton: + text: 'System Info' + on_press: + app.root.ids.workspace.transition.direction = 'up' + app.root.ids.workspace.current = 'screen_credits' + Spacer: + + Spacer: + + LeftLabel: + font_size: 13 + font_name: 'RobotoMono-Regular' + text: util.app_creators + + + +<ReturnToMain@TextButton>: + text: 'BACK' + dir: 'right' + + on_press: + app.root.ids.workspace.transition.direction = self.dir + app.root.ids.workspace.current = 'screen_start' + +<SpaceCreateSlide>: + HPage: + HPageBox: + HPageBody: + TextButton: + text: 'BACK' + on_press: + app.root.ids.workspace.current = 'screen_start' + app.root.ids.workspace.transition.direction = 'right' + + HTitle: + text: 'Create Slide' + HDivider: + + ImagePlot: + size: 600, 600 + id: plot + + HDivider: + + HStack: + LeftLabel: + text: 'IP address' + ChipInput: + hint_text: 'Ip Address' + callback: root.set_ip + + HStack: + TextButton: + id: btn_start + text: 'Start Webcam' + on_press: + root.toggle_webcam() + TextButton: + text: 'Capture Image' + on_press: + root.capture() +<FilterApply>: + size_hint: None, None + size: 400, 80 + + radius: 8 + padding: 5 + + text: '' + + canvas.before: + Color: + rgba: 1, 1, 1, 1 + Rectangle: + size: self.size[0] + 2 * self.radius, self.size[1] + 2 * self.radius + pos: self.pos[0] - self.radius, self.pos[1] - 1.5 * self.radius + texture: color.createBoxShadow(self.size[0] + self.radius, self.size[1] + self.radius, 0.8 * self.radius, 0.3) + + Color: + rgba: 1, 1, 1, 1 + Rectangle: + size: self.size + pos: self.pos + + LeftLabel: + font_name: 'RobotoMono-Regular' + + color: .4, .4, .4, 1 + text: root.text + + HSpacer: + height: 10 + HStack: + TextButton: + id: btn_train + text: 'Analyze' + on_press: + root.analysis_callback() + TextButton: + id: btn_reset + text: 'Reset' + on_press: + root.reset() + + + +<SpaceAnalyze>: + HPage: + HPageBox: + HPageBody: + TextButton: + text: 'BACK' + + on_press: + app.root.ids.workspace.transition.direction = 'right' + app.root.ids.workspace.current = 'screen_start' + + HTitle: + text: 'Analyze Slide' + + HDivider: + + HStack: + ImageLoader: + size: 450, 450 + id: slide + ImagePlot: + size: 450, 450 + id: sub_sample + + TextButton: + text: 'Select Slide' + on_press: + root.ids.slide.popup_selectImage() + + HDivider: + + LeftLabel: + font_name: 'RobotoMono-Regular' + text: 'REPORT' + bold: True + LeftLabel: + font_name: 'RobotoMono-Regular' + id: info + text: '' + + HDivider: + + TextButton: + text: 'Reload Filters' + on_press: + root.loadFilters() + HStack: + id: filter_list + +<FilterTrain>: + size_hint: None, None + size: 600, 400 + + radius: 8 + padding: 5 + + text: '' + + canvas.before: + Color: + rgba: 1, 1, 1, 1 + Rectangle: + size: self.size[0] + 2 * self.radius, self.size[1] + 2 * self.radius + pos: self.pos[0] - self.radius, self.pos[1] - 1.5 * self.radius + texture: color.createBoxShadow(self.size[0] + self.radius, self.size[1] + self.radius, 0.8 * self.radius, 0.3) + + Color: + rgba: 1, 1, 1, 1 + Rectangle: + size: self.size + pos: self.pos + + LeftLabel: + font_name: 'RobotoMono-Regular' + + color: .4, .4, .4, 1 + text: root.text + + HSpacer: + height: 10 + HStack: + TextButton: + id: btn_train + text: 'Train' + on_press: + root.train() + root.ids.btn_save.text = 'Save' + TextButton: + id: btn_save + text: '' + on_press: + text: 'Saving ...' + root.save() + +<SpaceTrain>: + HPage: + HPageBox: + HPageBody: + ReturnToMain: + dir: 'left' + + HTitle: + text: 'Create Filter' + + HDivider: + + HStack: + ImagePlot: + id: plot + + HStack: + TextButton: + text: 'Load Filters' + on_press: + root.load_filters() + + HDivider: + HStack: + id: filter_editor + +<SpaceFixedBody@Body>: + size_hint: .9, .8 + size_hint_max: 1000, 800 + size_hint_min: 500, 500 + + + +<SpaceCategorize>: + slide_img: None + + HPage: + HPageBox: + HPageBody: + TextButton: + text: 'BACK' + dir: 'right' + + on_press: + app.root.ids.workspace.transition.direction = self.dir + app.root.ids.workspace.current = 'screen_start' + + HTitle: + text: 'Categorize' + + StackLayout: + size_hint: 1, None + width: self.minimum_width + height: self.minimum_height + spacing: 10 + + ImageLoader: + size: 350, 350 + id: slide + + ImageLoader: + size: 350, 350 + id: sub_sample + +<SpaceCredits>: + SpaceFixedBody: + ReturnToMain: + dir: 'down' + Label: + color: .4, .4, .4, 1 + text: util.app_credits + font_name: 'RobotoMono-Regular' + size_hint: 1, .5 + +<MainWindow>: + title: 'MicroLab - DeepStain' + WorkSpace: + id: workspace \ No newline at end of file